From 2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 9 May 2016 14:22:11 +0200 Subject: BASELINE: Update Chromium to 51.0.2704.41 Also adds in all smaller components by reversing logic for exclusion. Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422 Reviewed-by: Joerg Bornemann --- chromium/content/BUILD.gn | 29 +- chromium/content/DEPS | 24 +- chromium/content/OWNERS | 1 - chromium/content/README | 6 - chromium/content/README.md | 11 + chromium/content/app/BUILD.gn | 43 +- chromium/content/app/DEPS | 3 +- .../content/app/android/child_process_service.cc | 19 +- chromium/content/app/android/content_main.cc | 1 + chromium/content/app/android/download_main.cc | 21 + .../content/app/android/library_loader_hooks.cc | 13 +- chromium/content/app/content_main_runner.cc | 213 +- chromium/content/app/mojo/mojo_init.cc | 32 +- chromium/content/app/strings/content_strings.grd | 200 +- .../strings/translations/content_strings_am.xtb | 58 +- .../strings/translations/content_strings_ar.xtb | 58 +- .../strings/translations/content_strings_bg.xtb | 58 +- .../strings/translations/content_strings_bn.xtb | 60 +- .../strings/translations/content_strings_ca.xtb | 58 +- .../strings/translations/content_strings_cs.xtb | 58 +- .../strings/translations/content_strings_da.xtb | 60 +- .../strings/translations/content_strings_de.xtb | 58 +- .../strings/translations/content_strings_el.xtb | 60 +- .../strings/translations/content_strings_en-GB.xtb | 58 +- .../translations/content_strings_es-419.xtb | 58 +- .../strings/translations/content_strings_es.xtb | 58 +- .../strings/translations/content_strings_et.xtb | 58 +- .../strings/translations/content_strings_fa.xtb | 62 +- .../strings/translations/content_strings_fi.xtb | 58 +- .../strings/translations/content_strings_fil.xtb | 58 +- .../strings/translations/content_strings_fr.xtb | 58 +- .../strings/translations/content_strings_gu.xtb | 58 +- .../strings/translations/content_strings_hi.xtb | 58 +- .../strings/translations/content_strings_hr.xtb | 58 +- .../strings/translations/content_strings_hu.xtb | 58 +- .../strings/translations/content_strings_id.xtb | 58 +- .../strings/translations/content_strings_it.xtb | 58 +- .../strings/translations/content_strings_iw.xtb | 58 +- .../strings/translations/content_strings_ja.xtb | 58 +- .../strings/translations/content_strings_kn.xtb | 58 +- .../strings/translations/content_strings_ko.xtb | 58 +- .../strings/translations/content_strings_lt.xtb | 58 +- .../strings/translations/content_strings_lv.xtb | 58 +- .../strings/translations/content_strings_ml.xtb | 62 +- .../strings/translations/content_strings_mr.xtb | 58 +- .../strings/translations/content_strings_ms.xtb | 58 +- .../strings/translations/content_strings_nl.xtb | 58 +- .../strings/translations/content_strings_no.xtb | 58 +- .../strings/translations/content_strings_pl.xtb | 58 +- .../strings/translations/content_strings_pt-BR.xtb | 58 +- .../strings/translations/content_strings_pt-PT.xtb | 58 +- .../strings/translations/content_strings_ro.xtb | 58 +- .../strings/translations/content_strings_ru.xtb | 58 +- .../strings/translations/content_strings_sk.xtb | 58 +- .../strings/translations/content_strings_sl.xtb | 58 +- .../strings/translations/content_strings_sr.xtb | 58 +- .../strings/translations/content_strings_sv.xtb | 58 +- .../strings/translations/content_strings_sw.xtb | 58 +- .../strings/translations/content_strings_ta.xtb | 58 +- .../strings/translations/content_strings_te.xtb | 58 +- .../strings/translations/content_strings_th.xtb | 58 +- .../strings/translations/content_strings_tr.xtb | 58 +- .../strings/translations/content_strings_uk.xtb | 58 +- .../strings/translations/content_strings_vi.xtb | 58 +- .../strings/translations/content_strings_zh-CN.xtb | 58 +- .../strings/translations/content_strings_zh-TW.xtb | 58 +- chromium/content/browser/BUILD.gn | 263 ++- chromium/content/browser/DEPS | 15 +- .../accessibility_event_recorder_win.cc | 2 +- .../accessibility_ipc_error_browsertest.cc | 1 - .../accessibility_tree_formatter_android.cc | 10 + .../accessibility_tree_formatter_auralinux.cc | 3 +- .../accessibility_tree_formatter_blink.cc | 47 +- .../accessibility_tree_formatter_mac.mm | 8 +- .../accessibility_tree_formatter_win.cc | 102 +- .../android_hit_testing_browsertest.cc | 79 - .../browser/accessibility/browser_accessibility.cc | 301 ++- .../browser/accessibility/browser_accessibility.h | 53 +- .../accessibility/browser_accessibility_android.cc | 437 ++++- .../accessibility/browser_accessibility_android.h | 4 +- .../browser_accessibility_auralinux.cc | 37 +- .../browser_accessibility_auralinux.h | 7 + .../accessibility/browser_accessibility_cocoa.h | 34 +- .../accessibility/browser_accessibility_cocoa.mm | 1369 ++++++++----- .../accessibility/browser_accessibility_mac.h | 8 + .../accessibility/browser_accessibility_mac.mm | 14 +- .../browser_accessibility_mac_unittest.mm | 3 +- .../accessibility/browser_accessibility_manager.cc | 438 ++++- .../accessibility/browser_accessibility_manager.h | 126 +- .../browser_accessibility_manager_android.cc | 312 +-- .../browser_accessibility_manager_android.h | 12 +- .../browser_accessibility_manager_mac.h | 29 +- .../browser_accessibility_manager_mac.mm | 205 +- .../browser_accessibility_manager_unittest.cc | 367 +++- .../browser_accessibility_manager_win.cc | 145 +- .../browser_accessibility_manager_win.h | 24 +- .../browser_accessibility_state_impl.cc | 2 +- .../browser_accessibility_state_impl_mac.mm | 44 + .../browser_accessibility_state_impl_win.cc | 1 + .../accessibility/browser_accessibility_win.cc | 669 +++++-- .../accessibility/browser_accessibility_win.h | 61 +- .../browser_accessibility_win_unittest.cc | 342 +++- .../dump_accessibility_browsertest_base.cc | 168 +- .../dump_accessibility_browsertest_base.h | 8 +- .../dump_accessibility_events_browsertest.cc | 26 +- .../dump_accessibility_tree_browsertest.cc | 102 +- .../accessibility/hit_testing_browsertest.cc | 171 ++ .../one_shot_accessibility_tree_search.cc | 246 +++ .../one_shot_accessibility_tree_search.h | 41 + .../site_per_process_accessibility_browsertest.cc | 14 +- .../accessibility/snapshot_ax_tree_browsertest.cc | 66 + .../android/browser_surface_texture_manager.h | 8 +- .../android/child_process_launcher_android.cc | 56 +- .../android/child_process_launcher_android.h | 8 + .../android/composited_touch_handle_drawable.cc | 2 +- .../browser/android/content_startup_flags.cc | 9 - .../content/browser/android/content_video_view.cc | 111 +- .../content/browser/android/content_video_view.h | 88 +- .../browser/android/content_view_core_impl.cc | 99 +- .../browser/android/content_view_core_impl.h | 29 +- .../android/content_view_core_impl_observer.h | 22 + .../browser/android/content_view_render_view.cc | 16 +- .../browser/android/content_view_render_view.h | 7 - .../android/download_controller_android_impl.cc | 101 +- .../android/download_controller_android_impl.h | 14 +- .../in_process/context_provider_in_process.cc | 53 +- .../in_process/context_provider_in_process.h | 11 +- .../synchronous_compositor_factory_impl.cc | 193 +- .../synchronous_compositor_factory_impl.h | 25 +- .../in_process/synchronous_compositor_impl.cc | 60 +- .../in_process/synchronous_compositor_impl.h | 17 +- .../synchronous_compositor_renderer_statics.cc | 15 + .../synchronous_compositor_renderer_statics.h | 16 + .../in_process/synchronous_input_event_filter.cc | 38 +- .../in_process/synchronous_input_event_filter.h | 18 +- .../android/in_process_surface_texture_manager.h | 8 +- chromium/content/browser/android/java/OWNERS | 2 +- .../android/service_registry_android_impl.cc | 111 ++ .../android/service_registry_android_impl.h | 53 + .../browser/android/synchronous_compositor_base.cc | 32 +- .../browser/android/synchronous_compositor_base.h | 3 + .../browser/android/synchronous_compositor_host.cc | 140 +- .../browser/android/synchronous_compositor_host.h | 19 +- .../browser/android/web_contents_observer_proxy.cc | 24 +- .../browser/android/web_contents_observer_proxy.h | 4 +- .../content/browser/appcache/appcache_database.cc | 2 + .../content/browser/appcache/appcache_database.h | 1 + .../browser/appcache/appcache_disk_cache.cc | 2 + .../content/browser/appcache/appcache_disk_cache.h | 2 + .../browser/appcache/appcache_interceptor.cc | 11 +- .../browser/appcache/appcache_interceptor.h | 4 +- .../browser/appcache/appcache_internals_ui.cc | 2 +- .../browser/appcache/appcache_request_handler.cc | 2 + .../browser/appcache/appcache_request_handler.h | 7 + .../content/browser/appcache/appcache_response.cc | 30 +- .../content/browser/appcache/appcache_response.h | 40 +- .../browser/appcache/appcache_service_impl.cc | 5 +- .../browser/appcache/appcache_service_unittest.cc | 4 +- .../browser/appcache/appcache_storage_impl.cc | 24 +- .../browser/appcache/appcache_update_job.cc | 8 +- .../content/browser/appcache/appcache_update_job.h | 1 + .../browser/appcache/appcache_url_request_job.cc | 6 + .../browser/appcache/appcache_url_request_job.h | 1 + .../appcache/chrome_appcache_service_unittest.cc | 9 +- .../browser/appcache/mock_appcache_storage.cc | 8 +- .../browser/background_sync/background_sync.proto | 16 +- .../background_sync/background_sync_browsertest.cc | 299 +-- .../background_sync_context_impl.cc | 13 +- .../background_sync/background_sync_context_impl.h | 12 +- .../background_sync/background_sync_manager.cc | 808 +++----- .../background_sync/background_sync_manager.h | 184 +- .../background_sync_manager_unittest.cc | 1048 ++-------- .../background_sync/background_sync_metrics.cc | 101 +- .../background_sync/background_sync_metrics.h | 15 +- .../background_sync_power_observer.cc | 62 - .../background_sync_power_observer.h | 51 - .../background_sync_power_observer_unittest.cc | 72 - .../background_sync_registration.cc | 62 +- .../background_sync/background_sync_registration.h | 35 +- .../background_sync_registration_handle.cc | 53 - .../background_sync_registration_handle.h | 95 - .../background_sync_registration_options.cc | 4 +- .../background_sync_registration_options.h | 3 - .../background_sync_service_impl.cc | 216 +-- .../background_sync/background_sync_service_impl.h | 46 +- .../background_sync_service_impl_unittest.cc | 257 +-- .../background_sync/background_sync_status.h | 3 +- chromium/content/browser/bad_message.h | 13 + .../battery_monitor_integration_browsertest.cc | 7 + .../blob_async_builder_host_unittest.cc | 476 ++++- ...lob_async_transport_request_builder_unittest.cc | 356 ++++ .../blob_async_transport_strategy_unittest.cc | 458 ----- .../browser/blob_storage/blob_dispatcher_host.cc | 370 ++++ .../browser/blob_storage/blob_dispatcher_host.h | 148 ++ .../blob_storage/blob_dispatcher_host_unittest.cc | 1199 ++++++++++++ .../blob_storage/blob_storage_registry_unittest.cc | 27 +- chromium/content/browser/bluetooth/README.md | 24 + .../bluetooth/bluetooth_allowed_devices_map.cc | 65 +- .../bluetooth/bluetooth_allowed_devices_map.h | 29 +- .../bluetooth_allowed_devices_map_unittest.cc | 355 +++- .../browser/bluetooth/bluetooth_blacklist.cc | 185 ++ .../browser/bluetooth/bluetooth_blacklist.h | 104 + .../bluetooth/bluetooth_blacklist_unittest.cc | 398 ++++ .../browser/bluetooth/bluetooth_dispatcher_host.cc | 849 +++++--- .../browser/bluetooth/bluetooth_dispatcher_host.h | 105 +- .../content/browser/bluetooth/bluetooth_metrics.cc | 40 +- .../content/browser/bluetooth/bluetooth_metrics.h | 22 + .../bluetooth/web_bluetooth_service_impl.cc | 156 ++ .../browser/bluetooth/web_bluetooth_service_impl.h | 77 + .../browser/browser_child_process_host_impl.cc | 80 +- .../browser/browser_child_process_host_impl.h | 9 +- chromium/content/browser/browser_context.cc | 66 +- chromium/content/browser/browser_main.cc | 1 + chromium/content/browser/browser_main_loop.cc | 177 +- chromium/content/browser/browser_main_loop.h | 26 +- chromium/content/browser/browser_main_runner.cc | 95 +- .../browser_plugin/browser_plugin_embedder.cc | 4 +- .../browser/browser_plugin/browser_plugin_guest.cc | 102 +- .../browser/browser_plugin/browser_plugin_guest.h | 26 +- .../content/browser/browser_process_sub_thread.cc | 2 - .../content/browser/browser_process_sub_thread.h | 1 + .../browser/browser_side_navigation_browsertest.cc | 46 +- chromium/content/browser/browser_thread_impl.cc | 36 +- chromium/content/browser/browsing_instance.cc | 53 +- chromium/content/browser/browsing_instance.h | 39 +- .../content/browser/cache_storage/cache_storage.cc | 353 ++-- .../content/browser/cache_storage/cache_storage.h | 79 +- .../browser/cache_storage/cache_storage.proto | 1 + .../cache_storage_blob_to_disk_cache.cc | 3 +- .../cache_storage_blob_to_disk_cache.h | 16 +- .../cache_storage_blob_to_disk_cache_unittest.cc | 2 +- .../browser/cache_storage/cache_storage_cache.cc | 389 ++-- .../browser/cache_storage/cache_storage_cache.h | 90 +- .../cache_storage/cache_storage_cache_unittest.cc | 320 ++- .../cache_storage/cache_storage_context_impl.cc | 18 +- .../cache_storage/cache_storage_context_impl.h | 8 +- .../cache_storage/cache_storage_dispatcher_host.cc | 62 +- .../cache_storage/cache_storage_dispatcher_host.h | 30 +- .../browser/cache_storage/cache_storage_manager.cc | 212 +- .../browser/cache_storage/cache_storage_manager.h | 20 +- .../cache_storage_manager_unittest.cc | 296 ++- .../cache_storage/cache_storage_unittest.cc | 26 +- chromium/content/browser/child_process_launcher.cc | 121 +- chromium/content/browser/child_process_launcher.h | 13 +- .../browser/child_process_security_policy_impl.cc | 10 +- .../child_process_security_policy_unittest.cc | 9 +- .../browser/cocoa/system_hotkey_helper_mac.h | 1 + .../content/browser/cocoa/system_hotkey_map.mm | 29 +- .../browser/cocoa/system_hotkey_map_unittest.mm | 10 +- .../compositor/browser_compositor_output_surface.h | 2 +- ...r_compositor_overlay_candidate_validator_mac.mm | 9 +- .../compositor/browser_compositor_view_mac.h | 78 - .../compositor/browser_compositor_view_mac.mm | 128 -- .../content/browser/compositor/buffer_queue.cc | 25 +- chromium/content/browser/compositor/buffer_queue.h | 9 +- .../browser/compositor/buffer_queue_unittest.cc | 9 +- .../browser/compositor/delegated_frame_host.cc | 1085 ----------- .../browser/compositor/delegated_frame_host.h | 357 ---- chromium/content/browser/compositor/gl_helper.cc | 1226 ++++++++++++ chromium/content/browser/compositor/gl_helper.h | 379 ++++ .../browser/compositor/gl_helper_benchmark.cc | 247 +++ .../compositor/gl_helper_readback_support.cc | 172 ++ .../compositor/gl_helper_readback_support.h | 75 + .../browser/compositor/gl_helper_scaling.cc | 881 +++++++++ .../content/browser/compositor/gl_helper_scaling.h | 206 ++ .../browser/compositor/gl_helper_unittest.cc | 1828 ++++++++++++++++++ .../gpu_browser_compositor_output_surface.cc | 4 +- .../gpu_browser_compositor_output_surface.h | 7 +- .../compositor/gpu_process_transport_factory.cc | 125 +- .../compositor/gpu_process_transport_factory.h | 7 +- ...urfaceless_browser_compositor_output_surface.cc | 4 +- ...surfaceless_browser_compositor_output_surface.h | 9 +- .../content/browser/compositor/owned_mailbox.cc | 2 +- .../browser/compositor/reflector_texture.cc | 2 +- chromium/content/browser/compositor/resize_lock.cc | 35 - chromium/content/browser/compositor/resize_lock.h | 37 - .../compositor/software_output_device_mac.h | 4 +- .../compositor/software_output_device_mac.mm | 4 +- .../compositor/software_output_device_win.cc | 10 +- .../compositor/software_output_device_win.h | 4 +- .../content/browser/compositor/surface_utils.cc | 177 +- .../content/browser/compositor/surface_utils.h | 12 + .../browser/cross_site_transfer_browsertest.cc | 6 +- chromium/content/browser/database_util_unittest.cc | 27 - chromium/content/browser/device_monitor_mac.h | 51 - chromium/content/browser/device_monitor_mac.mm | 568 ------ chromium/content/browser/device_monitor_udev.cc | 89 - chromium/content/browser/device_monitor_udev.h | 44 - .../data_fetcher_shared_memory_mac.cc | 1 - .../data_fetcher_shared_memory_win.cc | 3 +- .../device_sensors/device_light_message_filter.cc | 43 +- .../device_sensors/device_light_message_filter.h | 12 +- .../device_sensors/device_motion_message_filter.cc | 45 +- .../device_sensors/device_motion_message_filter.h | 12 +- .../device_orientation_absolute_message_filter.cc | 42 +- .../device_orientation_absolute_message_filter.h | 13 +- .../device_orientation_message_filter.cc | 47 +- .../device_orientation_message_filter.h | 12 +- .../device_sensors/device_sensor_message_filter.cc | 44 + .../device_sensors/device_sensor_message_filter.h | 40 + .../device_sensors/sensor_manager_android.cc | 1 - .../device_sensors/sensor_manager_chromeos.cc | 1 - .../sensor_manager_chromeos_unittest.cc | 1 - .../devtools/browser_devtools_agent_host.cc | 5 +- .../browser/devtools/devtools_agent_host_impl.cc | 7 +- .../browser/devtools/devtools_agent_host_impl.h | 2 +- .../devtools/devtools_frame_trace_recorder.cc | 2 +- .../devtools/devtools_frame_trace_recorder.h | 1 + .../content/browser/devtools/devtools_manager.h | 1 + .../browser/devtools/devtools_netlog_observer.cc | 4 +- .../protocol/devtools_protocol_browsertest.cc | 32 +- .../devtools/protocol/devtools_protocol_client.h | 1 + .../devtools_protocol_handler_generator.py | 1 + .../browser/devtools/protocol/emulation_handler.cc | 43 +- .../browser/devtools/protocol/emulation_handler.h | 26 +- .../browser/devtools/protocol/input_handler.cc | 11 +- .../browser/devtools/protocol/inspector_handler.cc | 5 +- .../browser/devtools/protocol/inspector_handler.h | 1 + .../browser/devtools/protocol/memory_handler.cc | 6 +- .../browser/devtools/protocol/network_handler.cc | 34 +- .../browser/devtools/protocol/page_handler.cc | 15 +- .../browser/devtools/protocol/page_handler.h | 3 +- .../devtools/protocol/service_worker_handler.cc | 80 +- .../devtools/protocol/service_worker_handler.h | 6 +- .../devtools/protocol/system_info_handler.cc | 7 +- .../browser/devtools/protocol/tethering_handler.cc | 9 +- .../browser/devtools/protocol/tracing_handler.cc | 118 +- .../browser/devtools/protocol/tracing_handler.h | 16 +- .../devtools/protocol/tracing_handler_unittest.cc | 73 + .../devtools/render_frame_devtools_agent_host.cc | 205 +- .../devtools/render_frame_devtools_agent_host.h | 16 +- .../devtools/service_worker_devtools_agent_host.cc | 4 + .../devtools/service_worker_devtools_agent_host.h | 1 + .../devtools/service_worker_devtools_manager.cc | 12 +- .../devtools/service_worker_devtools_manager.h | 6 +- .../shared_worker_devtools_manager_unittest.cc | 8 +- .../site_per_process_devtools_browsertest.cc | 65 +- .../browser/dom_storage/dom_storage_browsertest.cc | 1 - .../dom_storage/dom_storage_context_wrapper.cc | 194 +- .../dom_storage/dom_storage_context_wrapper.h | 20 +- .../browser/dom_storage/dom_storage_host.cc | 2 + .../content/browser/dom_storage/dom_storage_host.h | 1 + .../browser/dom_storage/dom_storage_namespace.cc | 2 + .../browser/dom_storage/dom_storage_namespace.h | 1 + .../dom_storage/session_storage_database.cc | 3 + chromium/content/browser/download/OWNERS | 2 - chromium/content/browser/download/base_file.cc | 201 +- chromium/content/browser/download/base_file.h | 190 +- .../content/browser/download/base_file_linux.cc | 7 +- chromium/content/browser/download/base_file_mac.cc | 9 +- .../content/browser/download/base_file_unittest.cc | 459 +++-- chromium/content/browser/download/base_file_win.cc | 11 +- .../content/browser/download/docs/save-page-as.md | 137 ++ .../browser/download/download_browsertest.cc | 999 ++++++++-- .../browser/download/download_create_info.cc | 21 +- .../browser/download/download_create_info.h | 50 +- .../download/download_destination_observer.h | 44 + chromium/content/browser/download/download_file.h | 29 +- .../browser/download/download_file_factory.cc | 13 +- .../browser/download/download_file_factory.h | 10 +- .../content/browser/download/download_file_impl.cc | 158 +- .../content/browser/download/download_file_impl.h | 66 +- .../browser/download/download_file_unittest.cc | 221 +-- .../download/download_interrupt_reasons_impl.cc | 3 +- .../browser/download/download_item_factory.h | 2 + .../content/browser/download/download_item_impl.cc | 1061 ++++++---- .../content/browser/download/download_item_impl.h | 392 ++-- .../download/download_item_impl_delegate.cc | 5 + .../browser/download/download_item_impl_delegate.h | 5 + .../download/download_item_impl_unittest.cc | 1180 ++++++++--- .../browser/download/download_manager_impl.cc | 305 ++- .../browser/download/download_manager_impl.h | 35 +- .../download/download_manager_impl_unittest.cc | 179 +- .../download/download_net_log_parameters.cc | 9 - .../browser/download/download_net_log_parameters.h | 3 - .../browser/download/download_request_core.cc | 527 +++-- .../browser/download/download_request_core.h | 88 +- .../browser/download/download_request_handle.cc | 86 +- .../browser/download/download_request_handle.h | 29 +- .../browser/download/download_resource_handler.cc | 105 +- .../browser/download/download_resource_handler.h | 25 +- .../content/browser/download/drag_download_file.cc | 4 +- .../browser/download/mhtml_generation_manager.cc | 5 +- .../content/browser/download/mock_download_file.h | 9 +- chromium/content/browser/download/save_file.cc | 23 +- chromium/content/browser/download/save_file.h | 1 - .../content/browser/download/save_file_manager.cc | 26 +- .../content/browser/download/save_file_manager.h | 14 +- .../browser/download/save_file_resource_handler.cc | 2 +- chromium/content/browser/download/save_item.cc | 4 +- chromium/content/browser/download/save_item.h | 8 +- chromium/content/browser/download/save_package.cc | 121 +- chromium/content/browser/download/save_package.h | 25 +- .../browser/download/save_package_unittest.cc | 3 +- chromium/content/browser/download/save_types.cc | 3 + chromium/content/browser/download/save_types.h | 8 +- .../content/browser/download/url_downloader.cc | 115 +- chromium/content/browser/download/url_downloader.h | 48 +- .../browser/fileapi/blob_reader_unittest.cc | 54 +- .../fileapi/blob_storage_context_unittest.cc | 274 +-- .../content/browser/fileapi/blob_storage_host.cc | 116 -- .../content/browser/fileapi/blob_storage_host.h | 75 - .../fileapi/blob_url_request_job_unittest.cc | 2 +- .../copy_or_move_operation_delegate_unittest.cc | 4 +- .../browser/fileapi/dragged_file_util_unittest.cc | 1 + .../file_system_dir_url_request_job_unittest.cc | 11 +- .../fileapi/file_system_quota_client_unittest.cc | 1 + .../file_system_url_request_job_unittest.cc | 1 - .../browser/fileapi/fileapi_message_filter.cc | 116 +- .../browser/fileapi/fileapi_message_filter.h | 25 +- .../fileapi/fileapi_message_filter_unittest.cc | 2 +- .../browser/fileapi/local_file_util_unittest.cc | 1 + .../browser/fileapi/native_file_util_unittest.cc | 1 + .../recursive_operation_delegate_unittest.cc | 1 + .../fileapi/sandbox_origin_database_unittest.cc | 1 + .../upload_file_system_file_element_reader.h | 1 + chromium/content/browser/frame_host/OWNERS | 1 + chromium/content/browser/frame_host/PRESUBMIT.py | 53 + .../frame_host/cross_process_frame_connector.cc | 145 +- .../frame_host/cross_process_frame_connector.h | 40 +- chromium/content/browser/frame_host/debug_urls.cc | 26 +- .../content/browser/frame_host/frame_mojo_shell.cc | 42 +- .../content/browser/frame_host/frame_mojo_shell.h | 34 +- .../browser/frame_host/frame_navigation_entry.cc | 19 +- .../browser/frame_host/frame_navigation_entry.h | 6 +- chromium/content/browser/frame_host/frame_tree.cc | 230 ++- chromium/content/browser/frame_host/frame_tree.h | 66 +- .../browser/frame_host/frame_tree_browsertest.cc | 31 +- .../content/browser/frame_host/frame_tree_node.cc | 156 +- .../content/browser/frame_host/frame_tree_node.h | 84 +- .../browser/frame_host/frame_tree_unittest.cc | 166 +- .../browser/frame_host/interstitial_page_impl.cc | 81 +- .../browser/frame_host/interstitial_page_impl.h | 16 +- .../frame_host/navigation_controller_android.cc | 4 +- .../frame_host/navigation_controller_android.h | 6 +- .../frame_host/navigation_controller_delegate.h | 5 +- .../frame_host/navigation_controller_impl.cc | 177 +- .../frame_host/navigation_controller_impl.h | 26 +- .../navigation_controller_impl_browsertest.cc | 424 +++- .../navigation_controller_impl_unittest.cc | 403 ++-- .../browser/frame_host/navigation_entry_impl.cc | 56 +- .../browser/frame_host/navigation_entry_impl.h | 7 +- .../frame_host/navigation_entry_impl_unittest.cc | 30 +- .../browser/frame_host/navigation_handle_impl.cc | 146 +- .../browser/frame_host/navigation_handle_impl.h | 76 +- .../navigation_handle_impl_browsertest.cc | 560 ++++++ .../frame_host/navigation_handle_impl_unittest.cc | 170 +- .../browser/frame_host/navigation_request.cc | 147 +- .../browser/frame_host/navigation_request.h | 36 +- chromium/content/browser/frame_host/navigator.cc | 5 - chromium/content/browser/frame_host/navigator.h | 8 +- .../content/browser/frame_host/navigator_impl.cc | 395 ++-- .../content/browser/frame_host/navigator_impl.h | 24 +- .../browser/frame_host/navigator_impl_unittest.cc | 168 +- .../frame_host/render_frame_host_delegate.cc | 7 - .../frame_host/render_frame_host_delegate.h | 11 +- .../frame_host/render_frame_host_factory.cc | 6 +- .../browser/frame_host/render_frame_host_factory.h | 4 +- .../browser/frame_host/render_frame_host_impl.cc | 650 ++++--- .../browser/frame_host/render_frame_host_impl.h | 225 ++- .../frame_host/render_frame_host_manager.cc | 924 +++++---- .../browser/frame_host/render_frame_host_manager.h | 125 +- .../render_frame_host_manager_browsertest.cc | 395 +++- .../render_frame_host_manager_unittest.cc | 438 ++--- .../frame_host/render_frame_message_filter.cc | 188 +- .../frame_host/render_frame_message_filter.h | 26 +- .../render_frame_message_filter_browsertest.cc | 67 +- .../browser/frame_host/render_frame_proxy_host.cc | 35 +- .../browser/frame_host/render_frame_proxy_host.h | 4 - .../render_widget_host_view_child_frame.cc | 234 ++- .../render_widget_host_view_child_frame.h | 66 +- ...render_widget_host_view_child_frame_unittest.cc | 47 +- .../frame_host/render_widget_host_view_guest.cc | 150 +- .../frame_host/render_widget_host_view_guest.h | 23 +- .../render_widget_host_view_guest_unittest.cc | 89 +- .../browser/frame_host/traced_frame_tree_node.cc | 69 + .../browser/frame_host/traced_frame_tree_node.h | 36 + .../gamepad/gamepad_platform_data_fetcher_linux.cc | 14 +- .../gamepad/gamepad_platform_data_fetcher_linux.h | 8 +- .../gamepad/gamepad_platform_data_fetcher_win.cc | 26 +- .../gamepad/gamepad_platform_data_fetcher_win.h | 7 +- .../content/browser/gamepad/gamepad_provider.cc | 3 + .../content/browser/gamepad/gamepad_provider.h | 1 + .../browser/geofencing/geofencing_manager.cc | 8 +- .../browser/geofencing/geofencing_service.h | 1 + .../browser/geolocation/fake_access_token_store.cc | 10 +- .../browser/geolocation/fake_access_token_store.h | 11 +- .../geolocation/geolocation_provider_impl.cc | 24 +- .../geolocation/geolocation_provider_impl.h | 10 +- .../geolocation_provider_impl_unittest.cc | 8 +- .../geolocation/geolocation_service_context.cc | 2 +- .../geolocation/geolocation_service_context.h | 6 +- .../geolocation/geolocation_service_impl.cc | 5 +- .../browser/geolocation/geolocation_service_impl.h | 20 +- .../geolocation/location_api_adapter_android.cc | 2 +- .../geolocation/location_api_adapter_android.h | 23 +- .../browser/geolocation/location_arbitrator.h | 2 +- .../geolocation/location_arbitrator_impl.cc | 32 +- .../browser/geolocation/location_arbitrator_impl.h | 6 +- .../location_arbitrator_impl_unittest.cc | 4 +- .../geolocation/mock_location_arbitrator.cc | 2 +- .../browser/geolocation/mock_location_arbitrator.h | 2 +- .../geolocation/network_location_provider.cc | 18 +- .../geolocation/network_location_provider.h | 2 +- .../network_location_provider_unittest.cc | 6 +- .../geolocation/network_location_request.cc | 58 +- .../browser/geolocation/network_location_request.h | 6 +- chromium/content/browser/geolocation/wifi_data.cc | 2 + chromium/content/browser/geolocation/wifi_data.h | 1 + .../browser/geolocation/wifi_data_provider.cc | 17 +- .../browser/geolocation/wifi_data_provider.h | 16 +- .../geolocation/wifi_data_provider_chromeos.cc | 18 +- .../geolocation/wifi_data_provider_chromeos.h | 1 + .../wifi_data_provider_chromeos_unittest.cc | 17 +- .../geolocation/wifi_data_provider_common.cc | 2 +- .../wifi_data_provider_common_unittest.cc | 80 +- .../geolocation/wifi_data_provider_corewlan_mac.mm | 3 +- .../wifi_data_provider_linux_unittest.cc | 9 +- .../geolocation/wifi_data_provider_manager.h | 1 - .../browser/geolocation/wifi_data_provider_win.cc | 1 + .../geolocation/wifi_data_provider_win_unittest.cc | 5 +- .../gpu/browser_gpu_channel_host_factory.cc | 83 +- .../browser/gpu/browser_gpu_channel_host_factory.h | 25 +- .../gpu/browser_gpu_memory_buffer_manager.cc | 77 +- .../gpu/browser_gpu_memory_buffer_manager.h | 12 +- chromium/content/browser/gpu/compositor_util.cc | 42 +- chromium/content/browser/gpu/compositor_util.h | 7 - .../browser/gpu/gpu_arc_video_service_host.cc | 76 - .../browser/gpu/gpu_arc_video_service_host.h | 54 - .../content/browser/gpu/gpu_data_manager_impl.cc | 29 +- .../content/browser/gpu/gpu_data_manager_impl.h | 22 +- .../browser/gpu/gpu_data_manager_impl_private.cc | 224 +-- .../browser/gpu/gpu_data_manager_impl_private.h | 38 +- .../gpu/gpu_data_manager_impl_private_unittest.cc | 66 +- chromium/content/browser/gpu/gpu_internals_ui.cc | 51 +- .../content/browser/gpu/gpu_ipc_browsertests.cc | 117 +- chromium/content/browser/gpu/gpu_process_host.cc | 245 +-- chromium/content/browser/gpu/gpu_process_host.h | 77 +- .../browser/gpu/gpu_process_host_ui_shim.cc | 18 +- .../content/browser/gpu/gpu_process_host_ui_shim.h | 19 +- .../content/browser/gpu/gpu_surface_tracker.cc | 55 +- chromium/content/browser/gpu/gpu_surface_tracker.h | 25 +- chromium/content/browser/histogram_controller.cc | 10 +- chromium/content/browser/host_zoom_map_impl.cc | 2 +- .../browser/indexed_db/indexed_db_backing_store.cc | 117 +- .../browser/indexed_db/indexed_db_backing_store.h | 11 +- .../indexed_db_backing_store_unittest.cc | 33 +- .../browser/indexed_db/indexed_db_browsertest.cc | 23 +- .../browser/indexed_db/indexed_db_callbacks.cc | 5 +- .../browser/indexed_db/indexed_db_context_impl.cc | 183 +- .../browser/indexed_db/indexed_db_context_impl.h | 34 +- .../browser/indexed_db/indexed_db_database.cc | 63 +- .../browser/indexed_db/indexed_db_database.h | 3 +- .../indexed_db/indexed_db_database_callbacks.cc | 2 +- .../indexed_db/indexed_db_database_unittest.cc | 38 +- .../indexed_db/indexed_db_dispatcher_host.cc | 217 +-- .../indexed_db/indexed_db_dispatcher_host.h | 15 +- .../browser/indexed_db/indexed_db_factory_impl.cc | 1 - .../indexed_db/indexed_db_factory_unittest.cc | 36 +- .../indexed_db/indexed_db_fake_backing_store.cc | 3 +- .../indexed_db/indexed_db_fake_backing_store.h | 3 +- .../browser/indexed_db/indexed_db_internals_ui.cc | 9 +- .../indexed_db/indexed_db_leveldb_coding.cc | 167 +- .../browser/indexed_db/indexed_db_leveldb_coding.h | 4 +- .../indexed_db_leveldb_coding_unittest.cc | 4 +- .../browser/indexed_db/indexed_db_metadata.cc | 7 +- .../browser/indexed_db/indexed_db_metadata.h | 11 +- .../indexed_db/indexed_db_pending_connection.cc | 3 + .../indexed_db/indexed_db_pending_connection.h | 1 + .../browser/indexed_db/indexed_db_quota_client.cc | 31 +- .../indexed_db/indexed_db_quota_client_unittest.cc | 5 +- .../indexed_db/indexed_db_transaction_unittest.cc | 2 +- .../browser/indexed_db/indexed_db_unittest.cc | 47 +- .../browser/indexed_db/leveldb/leveldb_database.cc | 32 + .../browser/indexed_db/leveldb/leveldb_database.h | 11 +- .../browser/indexed_db/leveldb_coding_scheme.md | 323 ++++ chromium/content/browser/leveldb_wrapper_impl.cc | 143 ++ chromium/content/browser/leveldb_wrapper_impl.h | 68 + .../browser/loader/async_resource_handler.cc | 223 ++- .../browser/loader/async_resource_handler.h | 11 +- .../loader/async_resource_handler_browsertest.cc | 10 +- .../browser/loader/async_revalidation_driver.cc | 4 +- .../browser/loader/async_revalidation_driver.h | 10 +- .../loader/async_revalidation_driver_unittest.cc | 24 +- .../browser/loader/async_revalidation_manager.cc | 6 +- .../browser/loader/async_revalidation_manager.h | 9 +- .../async_revalidation_manager_browsertest.cc | 43 +- .../loader/async_revalidation_manager_unittest.cc | 15 +- .../browser/loader/cross_site_resource_handler.cc | 7 +- .../browser/loader/cross_site_resource_handler.h | 2 +- .../cross_site_resource_handler_browsertest.cc | 19 +- .../browser/loader/detachable_resource_handler.cc | 2 +- .../browser/loader/detachable_resource_handler.h | 9 +- .../content/browser/loader/global_routing_id.h | 29 + .../browser/loader/layered_resource_handler.cc | 2 +- .../browser/loader/layered_resource_handler.h | 7 +- .../browser/loader/mime_type_resource_handler.cc | 94 +- .../browser/loader/mime_type_resource_handler.h | 9 +- .../loader/mime_type_resource_handler_unittest.cc | 116 +- .../browser/loader/navigation_resource_handler.cc | 11 +- .../browser/loader/navigation_resource_handler.h | 3 + .../browser/loader/navigation_resource_throttle.cc | 26 +- .../browser/loader/navigation_url_loader.cc | 6 +- .../content/browser/loader/navigation_url_loader.h | 10 +- .../loader/navigation_url_loader_delegate.h | 5 +- .../browser/loader/navigation_url_loader_factory.h | 7 +- .../browser/loader/navigation_url_loader_impl.cc | 13 +- .../browser/loader/navigation_url_loader_impl.h | 11 +- .../loader/navigation_url_loader_impl_core.cc | 11 +- .../loader/navigation_url_loader_impl_core.h | 10 +- .../loader/navigation_url_loader_unittest.cc | 77 +- .../loader/power_save_block_resource_throttle.h | 4 +- .../loader/redirect_to_file_resource_handler.cc | 8 +- .../loader/redirect_to_file_resource_handler.h | 7 +- chromium/content/browser/loader/resource_buffer.cc | 12 +- chromium/content/browser/loader/resource_buffer.h | 9 +- .../loader/resource_dispatcher_host_browsertest.cc | 14 +- .../loader/resource_dispatcher_host_impl.cc | 694 ++++--- .../browser/loader/resource_dispatcher_host_impl.h | 173 +- .../loader/resource_dispatcher_host_unittest.cc | 553 ++++-- chromium/content/browser/loader/resource_loader.cc | 60 +- chromium/content/browser/loader/resource_loader.h | 36 +- .../browser/loader/resource_loader_unittest.cc | 93 +- .../browser/loader/resource_message_filter.h | 3 +- .../browser/loader/resource_request_info_impl.cc | 17 +- .../browser/loader/resource_request_info_impl.h | 2 +- .../content/browser/loader/resource_scheduler.cc | 447 +---- .../content/browser/loader/resource_scheduler.h | 106 +- .../browser/loader/resource_scheduler_unittest.cc | 1908 ++---------------- .../browser/loader/stream_resource_handler.h | 3 +- .../browser/loader/temporary_file_stream.cc | 15 +- .../content/browser/loader/temporary_file_stream.h | 5 +- .../loader/temporary_file_stream_unittest.cc | 4 +- .../browser/loader/throttling_resource_handler.cc | 2 +- .../browser/loader/throttling_resource_handler.h | 2 +- .../browser/loader/upload_data_stream_builder.cc | 17 +- .../browser/loader/upload_data_stream_builder.h | 5 +- .../loader/upload_data_stream_builder_unittest.cc | 32 +- chromium/content/browser/mach_broker_mac.h | 62 +- chromium/content/browser/mach_broker_mac.mm | 173 +- .../content/browser/mach_broker_mac_unittest.cc | 112 +- chromium/content/browser/media/OWNERS | 11 +- chromium/content/browser/media/android/OWNERS | 3 - .../media/android/browser_demuxer_android.cc | 4 +- .../media/android/browser_demuxer_android.h | 2 +- .../media/android/browser_media_player_manager.cc | 140 +- .../media/android/browser_media_player_manager.h | 37 +- .../media/android/browser_media_session_manager.cc | 23 + .../media/android/browser_media_session_manager.h | 2 + .../media/android/browser_surface_view_manager.cc | 86 + .../media/android/browser_surface_view_manager.h | 54 + .../media/android/media_resource_getter_impl.cc | 4 +- .../content/browser/media/android/media_session.cc | 341 ---- .../content/browser/media/android/media_session.h | 181 -- .../media/android/media_session_browsertest.cc | 1225 ------------ .../browser/media/android/media_session_observer.h | 24 - .../media/android/media_session_uma_helper.cc | 66 - .../media/android/media_session_uma_helper.h | 52 - .../android/media_session_uma_helper_unittest.cc | 315 --- .../android/media_web_contents_observer_android.cc | 102 +- .../android/media_web_contents_observer_android.h | 48 +- .../media/android/provision_fetcher_impl.cc | 2 +- .../browser/media/android/provision_fetcher_impl.h | 10 +- .../browser/media/android/url_provision_fetcher.cc | 5 +- .../browser/media/android/url_provision_fetcher.h | 2 +- chromium/content/browser/media/audible_metrics.cc | 3 +- chromium/content/browser/media/audible_metrics.h | 6 +- .../browser/media/audible_metrics_unittest.cc | 61 +- .../browser/media/audio_stream_monitor_unittest.cc | 2 +- .../media/capture/audio_mirroring_manager.cc | 3 + .../media/capture/audio_mirroring_manager.h | 1 + .../capture/audio_mirroring_manager_unittest.cc | 40 +- .../media/capture/aura_window_capture_machine.cc | 171 +- .../media/capture/aura_window_capture_machine.h | 61 +- .../browser/media/capture/cursor_renderer.h | 2 + .../browser/media/capture/cursor_renderer_aura.cc | 56 +- .../browser/media/capture/cursor_renderer_aura.h | 13 +- .../media/capture/cursor_renderer_aura_unittest.cc | 55 +- .../browser/media/capture/cursor_renderer_mac.h | 48 + .../browser/media/capture/cursor_renderer_mac.mm | 189 ++ .../media/capture/desktop_capture_device.cc | 104 +- .../browser/media/capture/desktop_capture_device.h | 14 +- .../media/capture/desktop_capture_device_aura.cc | 13 +- .../media/capture/desktop_capture_device_aura.h | 9 +- .../desktop_capture_device_aura_unittest.cc | 33 +- .../capture/desktop_capture_device_uma_types.h | 3 + .../capture/desktop_capture_device_unittest.cc | 65 +- .../capture/web_contents_audio_input_stream.cc | 28 +- .../web_contents_audio_input_stream_unittest.cc | 4 +- .../media/capture/web_contents_audio_muter.cc | 12 +- .../browser/media/capture/web_contents_tracker.cc | 6 +- .../capture/web_contents_video_capture_device.cc | 260 +-- .../capture/web_contents_video_capture_device.h | 7 +- .../web_contents_video_capture_device_unittest.cc | 338 ++-- .../media/capture/window_activity_tracker.cc | 54 + .../media/capture/window_activity_tracker.h | 33 +- .../media/capture/window_activity_tracker_aura.cc | 39 +- .../media/capture/window_activity_tracker_aura.h | 11 - .../media/capture/window_activity_tracker_mac.h | 62 + .../media/capture/window_activity_tracker_mac.mm | 82 + .../browser/media/cdm/browser_cdm_manager.cc | 43 +- .../browser/media/cdm/browser_cdm_manager.h | 23 +- .../browser/media/encrypted_media_browsertest.cc | 20 +- .../content/browser/media/media_browsertest.cc | 10 +- .../browser/media/media_canplaytype_browsertest.cc | 791 +++++--- chromium/content/browser/media/media_internals.cc | 51 +- chromium/content/browser/media/media_internals.h | 5 +- .../content/browser/media/media_internals_proxy.cc | 2 +- .../content/browser/media/media_internals_proxy.h | 2 +- .../browser/media/media_internals_unittest.cc | 12 +- .../browser/media/media_web_contents_observer.cc | 118 +- .../browser/media/media_web_contents_observer.h | 41 +- chromium/content/browser/media/midi_host.cc | 10 +- chromium/content/browser/media/midi_host.h | 2 +- .../content/browser/media/midi_host_unittest.cc | 2 +- chromium/content/browser/media/session/OWNERS | 2 + .../content/browser/media/session/media_session.cc | 348 ++++ .../content/browser/media/session/media_session.h | 212 ++ .../media/session/media_session_browsertest.cc | 1203 ++++++++++++ .../media/session/media_session_controller.cc | 109 ++ .../media/session/media_session_controller.h | 71 + .../session/media_session_controller_unittest.cc | 214 ++ .../session/media_session_controllers_manager.cc | 94 + .../session/media_session_controllers_manager.h | 63 + .../browser/media/session/media_session_delegate.h | 26 + .../session/media_session_delegate_android.cc | 95 + .../media/session/media_session_delegate_android.h | 59 + .../media_session_delegate_android_browsertest.cc | 65 + .../session/media_session_delegate_default.cc | 62 + .../media_session_delegate_default_browsertest.cc | 49 + .../browser/media/session/media_session_observer.h | 29 + .../media/session/media_session_uma_helper.cc | 66 + .../media/session/media_session_uma_helper.h | 54 + .../session/media_session_uma_helper_unittest.cc | 315 +++ .../media_session_visibility_browsertest.cc | 266 +++ .../media/session/mock_media_session_observer.cc | 70 + .../media/session/mock_media_session_observer.h | 59 + chromium/content/browser/media/webrtc/OWNERS | 5 + .../webrtc_audio_debug_recordings_browsertest.cc | 280 +++ .../browser/media/webrtc/webrtc_browsertest.cc | 330 ++++ .../webrtc/webrtc_getusermedia_browsertest.cc | 779 ++++++++ .../browser/media/webrtc/webrtc_identity_store.cc | 325 ++++ .../browser/media/webrtc/webrtc_identity_store.h | 126 ++ .../media/webrtc/webrtc_identity_store_backend.cc | 610 ++++++ .../media/webrtc/webrtc_identity_store_backend.h | 122 ++ .../media/webrtc/webrtc_identity_store_unittest.cc | 396 ++++ .../browser/media/webrtc/webrtc_internals.cc | 522 +++++ .../browser/media/webrtc/webrtc_internals.h | 244 +++ .../media/webrtc/webrtc_internals_browsertest.cc | 884 +++++++++ .../webrtc/webrtc_internals_message_handler.cc | 133 ++ .../webrtc/webrtc_internals_message_handler.h | 52 + .../browser/media/webrtc/webrtc_internals_ui.cc | 44 + .../browser/media/webrtc/webrtc_internals_ui.h | 24 + .../media/webrtc/webrtc_internals_ui_observer.h | 29 + .../media/webrtc/webrtc_internals_unittest.cc | 344 ++++ .../webrtc/webrtc_ip_permissions_browsertest.cc | 180 ++ .../webrtc/webrtc_media_recorder_browsertest.cc | 153 ++ .../media/webrtc/webrtc_webcam_browsertest.cc | 103 + .../webrtc_audio_debug_recordings_browsertest.cc | 280 --- .../content/browser/media/webrtc_browsertest.cc | 330 ---- .../media/webrtc_getusermedia_browsertest.cc | 778 -------- .../content/browser/media/webrtc_identity_store.cc | 325 ---- .../content/browser/media/webrtc_identity_store.h | 126 -- .../browser/media/webrtc_identity_store_backend.cc | 609 ------ .../browser/media/webrtc_identity_store_backend.h | 123 -- .../media/webrtc_identity_store_unittest.cc | 394 ---- chromium/content/browser/media/webrtc_internals.cc | 490 ----- chromium/content/browser/media/webrtc_internals.h | 198 -- .../browser/media/webrtc_internals_browsertest.cc | 884 --------- .../media/webrtc_internals_message_handler.cc | 133 -- .../media/webrtc_internals_message_handler.h | 52 - .../content/browser/media/webrtc_internals_ui.cc | 44 - .../content/browser/media/webrtc_internals_ui.h | 24 - .../browser/media/webrtc_internals_ui_observer.h | 29 - .../browser/media/webrtc_internals_unittest.cc | 288 --- .../media/webrtc_ip_permissions_browsertest.cc | 180 -- .../media/webrtc_media_recorder_browsertest.cc | 153 -- .../browser/media/webrtc_webcam_browsertest.cc | 103 - .../browser/memory/memory_message_filter.cc | 7 +- .../content/browser/memory/memory_message_filter.h | 4 +- .../browser/memory/memory_pressure_controller.cc | 134 -- .../browser/memory/memory_pressure_controller.h | 66 - .../memory_pressure_controller_browsertest.cc | 274 --- .../memory/memory_pressure_controller_impl.cc | 135 ++ .../memory/memory_pressure_controller_impl.h | 88 + .../memory_pressure_controller_impl_browsertest.cc | 274 +++ chromium/content/browser/message_port_service.cc | 11 +- chromium/content/browser/message_port_service.h | 3 + chromium/content/browser/mojo/OWNERS | 13 - chromium/content/browser/mojo/constants.cc | 19 + chromium/content/browser/mojo/constants.h | 15 + .../browser/mojo/mojo_app_connection_impl.cc | 28 +- .../browser/mojo/mojo_app_connection_impl.h | 16 +- .../content/browser/mojo/mojo_application_host.cc | 55 +- .../content/browser/mojo/mojo_application_host.h | 11 +- .../content/browser/mojo/mojo_child_connection.cc | 137 ++ .../content/browser/mojo/mojo_child_connection.h | 36 + .../content/browser/mojo/mojo_shell_client_host.cc | 219 --- .../content/browser/mojo/mojo_shell_client_host.h | 48 - .../content/browser/mojo/mojo_shell_context.cc | 269 +-- chromium/content/browser/mojo/mojo_shell_context.h | 53 +- .../browser/mojo/renderer_capability_filter.cc | 22 - .../browser/mojo/service_registrar_android.cc | 2 +- .../browser/mojo/service_registry_android.cc | 102 - .../browser/mojo/service_registry_android.h | 56 - chromium/content/browser/mojo_shell_browsertest.cc | 33 +- chromium/content/browser/navigator_connect/OWNERS | 1 - .../navigator_connect_context_impl.cc | 266 --- .../navigator_connect_context_impl.h | 133 -- .../navigator_connect/service_port_service_impl.cc | 132 -- .../navigator_connect/service_port_service_impl.h | 85 - .../browser/net/quota_policy_cookie_store.cc | 18 +- .../net/quota_policy_cookie_store_unittest.cc | 8 +- .../browser/notifications/notification_database.h | 2 + .../notifications/notification_database_data.proto | 16 +- .../notification_database_data_conversions.cc | 49 +- .../notification_database_data_unittest.cc | 73 + .../notification_event_dispatcher_impl.cc | 277 ++- .../notification_event_dispatcher_impl.h | 12 +- .../notifications/notification_message_filter.cc | 55 +- .../notifications/notification_message_filter.h | 13 +- .../permissions/permission_service_context.cc | 2 +- .../permissions/permission_service_context.h | 17 +- .../browser/permissions/permission_service_impl.cc | 67 +- .../browser/permissions/permission_service_impl.h | 53 +- .../content/browser/plugin_data_remover_impl.cc | 85 +- chromium/content/browser/plugin_loader_posix.cc | 237 --- chromium/content/browser/plugin_loader_posix.h | 134 -- .../browser/plugin_loader_posix_unittest.cc | 426 ---- chromium/content/browser/plugin_process_host.cc | 442 ----- chromium/content/browser/plugin_process_host.h | 219 --- .../content/browser/plugin_process_host_mac.cc | 107 - chromium/content/browser/plugin_service_impl.cc | 473 +---- chromium/content/browser/plugin_service_impl.h | 93 - .../content/browser/power_save_blocker_impl.cc | 4 - chromium/content/browser/power_save_blocker_x11.cc | 67 +- .../content/browser/ppapi_plugin_process_host.cc | 25 +- .../content/browser/ppapi_plugin_process_host.h | 5 + .../presentation/presentation_service_impl.cc | 116 +- .../presentation/presentation_service_impl.h | 43 +- .../presentation_service_impl_unittest.cc | 185 +- .../presentation/presentation_type_converters.cc | 39 +- .../presentation/presentation_type_converters.h | 27 +- .../presentation_type_converters_unittest.cc | 9 +- .../push_messaging_message_filter.cc | 72 +- .../push_messaging/push_messaging_message_filter.h | 16 +- .../push_messaging/push_messaging_router.cc | 38 +- .../browser/push_messaging/push_messaging_router.h | 18 +- .../browser/quota/mock_quota_manager_proxy.cc | 10 + .../browser/quota/mock_quota_manager_proxy.h | 2 +- .../browser/quota/quota_backend_impl_unittest.cc | 1 + .../browser/quota/quota_manager_unittest.cc | 13 +- .../quota/quota_reservation_manager_unittest.cc | 1 + .../quota_temporary_storage_evictor_unittest.cc | 6 + .../browser/quota/storage_monitor_unittest.cc | 2 +- .../browser/quota/usage_tracker_unittest.cc | 2 +- chromium/content/browser/quota_dispatcher_host.cc | 2 +- chromium/content/browser/renderer_host/DEPS | 7 - .../renderer_host/browser_compositor_view_mac.h | 78 + .../renderer_host/browser_compositor_view_mac.mm | 120 ++ .../renderer_host/clipboard_message_filter.cc | 4 - .../renderer_host/compositor_impl_android.cc | 146 +- .../renderer_host/compositor_impl_android.h | 3 +- .../renderer_host/compositor_resize_lock_aura.h | 2 +- .../renderer_host/database_message_filter.cc | 9 +- .../browser/renderer_host/delegated_frame_host.cc | 881 +++++++++ .../browser/renderer_host/delegated_frame_host.h | 324 ++++ .../dwrite_font_proxy_message_filter_win.cc | 60 +- .../renderer_host/event_with_latency_info.h | 55 +- .../event_with_latency_info_unittest.cc | 109 -- .../browser/renderer_host/font_utils_linux.cc | 40 +- .../browser/renderer_host/gpu_message_filter.cc | 95 - .../browser/renderer_host/gpu_message_filter.h | 54 - .../browser/renderer_host/ime_adapter_android.cc | 54 +- .../browser/renderer_host/ime_adapter_android.h | 7 + .../input/composited_scrolling_browsertest.cc | 2 +- .../renderer_host/input/gesture_event_queue.cc | 22 + .../renderer_host/input/gesture_event_queue.h | 2 + .../input/gesture_event_queue_unittest.cc | 25 + .../browser/renderer_host/input/input_router.h | 4 + .../renderer_host/input/input_router_client.h | 7 + .../renderer_host/input/input_router_impl.cc | 150 +- .../renderer_host/input/input_router_impl.h | 41 +- .../input/input_router_impl_perftest.cc | 5 +- .../input/input_router_impl_unittest.cc | 109 +- .../input/mock_input_router_client.cc | 8 + .../renderer_host/input/mock_input_router_client.h | 3 + .../renderer_host/input/mouse_wheel_event_queue.cc | 288 +++ .../renderer_host/input/mouse_wheel_event_queue.h | 110 ++ .../input/mouse_wheel_event_queue_unittest.cc | 547 ++++++ .../input/non_blocking_event_browsertest.cc | 218 +++ .../input/render_widget_host_latency_tracker.cc | 47 +- .../input/render_widget_host_latency_tracker.h | 2 +- .../render_widget_host_latency_tracker_unittest.cc | 284 ++- .../renderer_host/input/synthetic_gesture.cc | 4 + .../renderer_host/input/synthetic_gesture.h | 2 - .../input/synthetic_gesture_controller.h | 2 +- .../input/synthetic_gesture_controller_unittest.cc | 41 +- .../input/synthetic_pointer_action.cc | 45 +- .../renderer_host/input/synthetic_pointer_action.h | 15 +- .../input/synthetic_smooth_move_gesture.cc | 3 + .../input/synthetic_smooth_move_gesture.h | 2 + .../input/touch_action_browsertest.cc | 2 +- .../browser/renderer_host/input/touch_emulator.cc | 3 +- .../browser/renderer_host/input/touch_emulator.h | 2 +- .../renderer_host/input/touch_emulator_unittest.cc | 7 +- .../renderer_host/input/touch_event_queue.cc | 24 +- .../renderer_host/input/touch_event_queue.h | 3 + .../input/touch_event_queue_unittest.cc | 98 +- .../renderer_host/input/touch_input_browsertest.cc | 101 +- ...selection_controller_client_aura_browsertest.cc | 299 ++- .../touchscreen_tap_suppression_controller.cc | 2 +- .../input/web_input_event_builders_android.cc | 78 +- .../input/web_input_event_builders_android.h | 37 +- .../web_input_event_builders_android_unittest.cc | 169 ++ .../input/web_input_event_builders_mac.mm | 9 +- .../input/web_input_event_builders_mac_unittest.mm | 188 +- .../renderer_host/input/web_input_event_util.cc | 110 -- .../renderer_host/input/web_input_event_util.h | 5 - .../input/web_input_event_util_unittest.cc | 8 - .../renderer_host/legacy_render_widget_host_win.cc | 10 +- .../content/browser/renderer_host/media/OWNERS | 4 +- .../media/audio_input_debug_writer.cc | 166 +- .../renderer_host/media/audio_input_debug_writer.h | 28 +- .../media/audio_input_debug_writer_unittest.cc | 270 +++ .../media/audio_input_device_manager.cc | 7 +- .../media/audio_input_device_manager.h | 5 +- .../media/audio_input_device_manager_unittest.cc | 17 +- .../media/audio_input_renderer_host.cc | 84 +- .../media/audio_input_renderer_host.h | 8 +- .../renderer_host/media/audio_input_sync_writer.cc | 11 +- .../renderer_host/media/audio_input_sync_writer.h | 4 +- .../media/audio_input_sync_writer_unittest.cc | 10 +- .../media/audio_output_device_enumerator.cc | 7 +- .../media/audio_output_device_enumerator.h | 4 +- .../audio_output_device_enumerator_unittest.cc | 7 +- .../renderer_host/media/audio_renderer_host.cc | 102 +- .../renderer_host/media/audio_renderer_host.h | 12 +- .../media/audio_renderer_host_unittest.cc | 20 +- .../renderer_host/media/audio_sync_reader.cc | 22 + .../renderer_host/media/audio_sync_reader.h | 7 +- .../media/media_stream_dispatcher_host_unittest.cc | 11 +- .../renderer_host/media/media_stream_manager.cc | 159 +- .../renderer_host/media/media_stream_manager.h | 26 +- .../media/media_stream_manager_unittest.cc | 22 +- .../media/media_stream_ui_controller_unittest.cc | 14 +- .../renderer_host/media/media_stream_ui_proxy.cc | 24 +- .../renderer_host/media/media_stream_ui_proxy.h | 13 +- .../media/media_stream_ui_proxy_unittest.cc | 67 +- .../media/peer_connection_tracker_host.cc | 2 +- .../media/video_capture_buffer_pool.cc | 451 ++--- .../media/video_capture_buffer_pool.h | 55 +- .../media/video_capture_buffer_pool_unittest.cc | 187 +- .../media/video_capture_controller.cc | 28 +- .../renderer_host/media/video_capture_controller.h | 13 +- .../media/video_capture_controller_event_handler.h | 3 +- .../media/video_capture_controller_unittest.cc | 80 +- .../media/video_capture_device_client.cc | 108 +- .../media/video_capture_device_client.h | 35 +- .../media/video_capture_device_client_unittest.cc | 69 +- .../media/video_capture_gpu_jpeg_decoder.cc | 26 +- .../media/video_capture_gpu_jpeg_decoder.h | 23 +- .../renderer_host/media/video_capture_host.cc | 21 +- .../renderer_host/media/video_capture_host.h | 4 + .../media/video_capture_host_unittest.cc | 11 +- .../renderer_host/media/video_capture_manager.cc | 184 +- .../renderer_host/media/video_capture_manager.h | 65 +- .../media/video_capture_manager_unittest.cc | 20 +- .../media/webrtc_identity_service_host.cc | 2 +- .../media/webrtc_identity_service_host_unittest.cc | 20 +- .../memory_benchmark_message_filter.cc | 41 - .../memory_benchmark_message_filter.h | 31 - .../native_web_keyboard_event_android.cc | 49 +- .../browser/renderer_host/overscroll_controller.cc | 90 +- .../browser/renderer_host/overscroll_controller.h | 8 +- .../renderer_host/p2p/socket_dispatcher_host.cc | 23 +- .../renderer_host/p2p/socket_dispatcher_host.h | 11 +- .../browser/renderer_host/p2p/socket_host.cc | 430 +---- .../browser/renderer_host/p2p/socket_host.h | 27 - .../browser/renderer_host/p2p/socket_host_tcp.cc | 17 +- .../renderer_host/p2p/socket_host_tcp_server.cc | 1 - .../renderer_host/p2p/socket_host_test_utils.cc | 9 +- .../renderer_host/p2p/socket_host_test_utils.h | 1 - .../browser/renderer_host/p2p/socket_host_udp.cc | 13 +- .../browser/renderer_host/p2p/socket_host_udp.h | 1 + .../renderer_host/p2p/socket_host_udp_unittest.cc | 4 +- .../renderer_host/p2p/socket_host_unittest.cc | 386 ---- .../pepper/browser_ppapi_host_impl.cc | 2 +- .../renderer_host/pepper/pepper_file_io_host.cc | 4 +- .../renderer_host/pepper/pepper_file_io_host.h | 1 + .../pepper/pepper_flash_file_message_filter.cc | 4 +- .../pepper/pepper_host_resolver_message_filter.cc | 2 +- .../pepper/pepper_internal_file_ref_backend.cc | 12 +- .../pepper/pepper_network_monitor_host.cc | 2 +- .../pepper/pepper_network_proxy_host.cc | 15 +- .../pepper/pepper_network_proxy_host.h | 1 + .../renderer_host/pepper/pepper_socket_utils.cc | 5 +- .../pepper_tcp_server_socket_message_filter.cc | 15 +- .../pepper/pepper_tcp_socket_message_filter.cc | 21 +- .../pepper/pepper_udp_socket_message_filter.cc | 31 +- .../pepper/pepper_udp_socket_message_filter.h | 6 +- .../renderer_host/pepper/quota_reservation.cc | 1 + .../pepper/quota_reservation_unittest.cc | 1 + .../browser/renderer_host/render_message_filter.cc | 134 +- .../browser/renderer_host/render_message_filter.h | 16 + .../renderer_host/render_process_host_impl.cc | 469 ++--- .../renderer_host/render_process_host_impl.h | 52 +- .../renderer_host/render_view_host_factory.cc | 3 + .../renderer_host/render_view_host_factory.h | 16 + .../browser/renderer_host/render_view_host_impl.cc | 85 +- .../browser/renderer_host/render_view_host_impl.h | 40 +- .../browser/renderer_host/render_widget_helper.cc | 26 +- .../browser/renderer_host/render_widget_helper.h | 7 - .../renderer_host/render_widget_host_delegate.cc | 7 - .../renderer_host/render_widget_host_delegate.h | 31 +- .../renderer_host/render_widget_host_impl.cc | 181 +- .../renderer_host/render_widget_host_impl.h | 50 +- .../render_widget_host_input_event_router.cc | 205 +- .../render_widget_host_input_event_router.h | 66 +- .../render_widget_host_owner_delegate.h | 4 - .../renderer_host/render_widget_host_unittest.cc | 16 +- .../render_widget_host_view_android.cc | 425 ++-- .../render_widget_host_view_android.h | 40 +- .../renderer_host/render_widget_host_view_aura.cc | 476 ++--- .../renderer_host/render_widget_host_view_aura.h | 64 +- .../render_widget_host_view_aura_unittest.cc | 921 ++++++++- .../renderer_host/render_widget_host_view_base.cc | 405 +--- .../renderer_host/render_widget_host_view_base.h | 94 +- .../render_widget_host_view_base_observer.cc | 14 + .../render_widget_host_view_base_observer.h | 32 + .../render_widget_host_view_browsertest.cc | 7 +- .../renderer_host/render_widget_host_view_mac.h | 50 +- .../renderer_host/render_widget_host_view_mac.mm | 365 ++-- .../render_widget_host_view_mac_unittest.mm | 118 +- .../renderer_host/render_widget_host_view_mus.cc | 34 +- .../renderer_host/render_widget_host_view_mus.h | 13 - .../content/browser/renderer_host/resize_lock.cc | 34 + .../content/browser/renderer_host/resize_lock.h | 37 + .../browser/renderer_host/sandbox_ipc_linux.cc | 63 +- .../browser/renderer_host/sandbox_ipc_linux.h | 8 +- .../text_input_client_mac_unittest.mm | 3 +- .../text_input_client_message_filter.h | 2 +- .../text_input_client_message_filter.mm | 11 +- .../browser/renderer_host/web_input_event_aura.cc | 16 +- .../renderer_host/web_input_event_aura_unittest.cc | 8 +- .../browser/renderer_host/websocket_blob_sender.cc | 284 +++ .../browser/renderer_host/websocket_blob_sender.h | 139 ++ .../websocket_blob_sender_unittest.cc | 446 +++++ .../renderer_host/websocket_dispatcher_host.cc | 31 +- .../renderer_host/websocket_dispatcher_host.h | 29 +- .../browser/renderer_host/websocket_host.cc | 206 +- .../content/browser/renderer_host/websocket_host.h | 10 + .../content/browser/resolve_proxy_msg_helper.cc | 2 +- .../browser/resolve_proxy_msg_helper_unittest.cc | 3 +- chromium/content/browser/resource_context_impl.cc | 10 +- .../browser/resources/gpu/browser_bridge.js | 17 - .../content/browser/resources/gpu/info_view.html | 15 +- .../content/browser/resources/gpu/info_view.js | 19 +- chromium/content/browser/resources/media/OWNERS | 4 +- .../browser/resources/media/dump_creator.js | 24 +- .../service_worker/serviceworker_internals.html | 5 +- .../service_worker/serviceworker_internals.js | 2 +- .../screen_orientation_browsertest.cc | 18 +- .../screen_orientation_delegate_win.cc | 133 ++ .../screen_orientation_delegate_win.h | 30 + .../browser/security_exploit_browsertest.cc | 136 +- .../service_worker/embedded_worker_instance.cc | 277 ++- .../service_worker/embedded_worker_instance.h | 36 +- .../embedded_worker_instance_unittest.cc | 199 +- .../service_worker/embedded_worker_test_helper.cc | 101 +- .../service_worker/embedded_worker_test_helper.h | 41 +- .../foreign_fetch_request_handler.cc | 25 +- .../service_worker/foreign_fetch_request_handler.h | 5 +- .../browser/service_worker/link_header_support.cc | 311 +++ .../browser/service_worker/link_header_support.h | 38 + .../service_worker/link_header_support_unittest.cc | 410 ++++ .../service_worker/service_worker_browsertest.cc | 400 +++- .../service_worker_cache_writer_unittest.cc | 43 +- .../service_worker/service_worker_client_utils.cc | 138 +- .../service_worker/service_worker_client_utils.h | 14 +- .../service_worker/service_worker_context_core.cc | 84 +- .../service_worker/service_worker_context_core.h | 26 +- .../service_worker_context_observer.h | 2 - .../service_worker_context_request_handler.cc | 4 + ...vice_worker_context_request_handler_unittest.cc | 27 + .../service_worker_context_watcher.cc | 14 +- .../service_worker_context_watcher.h | 3 +- .../service_worker_context_wrapper.cc | 73 +- .../service_worker_context_wrapper.h | 33 +- .../service_worker_controllee_request_handler.cc | 31 +- .../service_worker_controllee_request_handler.h | 7 +- ...e_worker_controllee_request_handler_unittest.cc | 21 +- .../service_worker/service_worker_database.cc | 15 + .../service_worker/service_worker_database.h | 3 + .../service_worker/service_worker_database.proto | 1 + .../service_worker_database_task_manager.h | 1 + .../service_worker_database_unittest.cc | 6 + .../service_worker/service_worker_disk_cache.cc | 6 +- .../service_worker/service_worker_disk_cache.h | 18 +- .../service_worker_dispatcher_host.cc | 428 ++-- .../service_worker_dispatcher_host.h | 78 +- .../service_worker_dispatcher_host_unittest.cc | 257 ++- .../service_worker_fetch_dispatcher.cc | 81 +- .../service_worker_fetch_dispatcher.h | 14 +- .../browser/service_worker/service_worker_handle.h | 1 + .../service_worker_handle_unittest.cc | 21 +- .../browser/service_worker/service_worker_info.cc | 10 +- .../browser/service_worker/service_worker_info.h | 5 +- .../service_worker/service_worker_internals_ui.cc | 81 +- .../service_worker/service_worker_internals_ui.h | 1 - .../service_worker_job_coordinator.cc | 3 + .../service_worker_job_coordinator.h | 1 + .../service_worker/service_worker_job_unittest.cc | 132 +- .../service_worker/service_worker_metrics.cc | 264 ++- .../service_worker/service_worker_metrics.h | 80 +- .../service_worker_process_manager.cc | 65 +- .../service_worker_process_manager.h | 12 +- .../service_worker_process_manager_unittest.cc | 11 +- .../service_worker/service_worker_provider_host.cc | 106 +- .../service_worker/service_worker_provider_host.h | 28 +- .../service_worker_read_from_cache_job.cc | 2 - .../service_worker_read_from_cache_job_unittest.cc | 53 +- .../service_worker/service_worker_register_job.cc | 101 +- .../service_worker/service_worker_register_job.h | 10 +- .../service_worker/service_worker_registration.cc | 34 +- .../service_worker/service_worker_registration.h | 14 +- .../service_worker_request_handler.cc | 9 +- .../service_worker_request_handler.h | 6 + .../service_worker/service_worker_storage.cc | 58 +- .../service_worker/service_worker_storage.h | 20 +- .../service_worker_storage_unittest.cc | 271 ++- .../service_worker_url_request_job.cc | 48 +- .../service_worker_url_request_job.h | 27 +- .../service_worker_url_request_job_unittest.cc | 54 +- .../service_worker/service_worker_version.cc | 800 +++----- .../service_worker/service_worker_version.h | 225 +-- .../service_worker_version_unittest.cc | 563 ++++-- .../service_worker_write_to_cache_job.cc | 27 +- .../service_worker_write_to_cache_job.h | 11 +- .../service_worker_write_to_cache_job_unittest.cc | 1 + .../browser/shared_worker/shared_worker_host.cc | 17 +- .../browser/shared_worker/shared_worker_host.h | 5 - .../shared_worker/shared_worker_instance.cc | 3 + .../browser/shared_worker/shared_worker_instance.h | 6 + .../shared_worker_instance_unittest.cc | 19 +- .../shared_worker/shared_worker_message_filter.cc | 17 - .../shared_worker/shared_worker_message_filter.h | 6 - .../shared_worker/shared_worker_service_impl.cc | 18 +- .../shared_worker/shared_worker_service_impl.h | 8 +- .../browser/shared_worker/worker_browsertest.cc | 6 +- chromium/content/browser/site_instance_impl.cc | 52 +- chromium/content/browser/site_instance_impl.h | 50 +- .../content/browser/site_instance_impl_unittest.cc | 355 ++-- .../browser/site_per_process_browsertest.cc | 2039 +++++++++++++++++--- .../browser/speech/speech_recognizer_impl.cc | 6 +- .../browser/speech/speech_recognizer_impl.h | 1 + .../content/browser/ssl/ssl_client_auth_handler.cc | 9 +- .../content/browser/ssl/ssl_client_auth_handler.h | 1 + chromium/content/browser/storage_partition_impl.cc | 73 +- chromium/content/browser/storage_partition_impl.h | 37 +- .../content/browser/storage_partition_impl_map.cc | 52 +- .../browser/streams/stream_url_request_job.cc | 33 +- .../browser/streams/stream_url_request_job.h | 5 +- .../content/browser/system_message_window_win.cc | 164 -- .../content/browser/system_message_window_win.h | 51 - .../browser/system_message_window_win_unittest.cc | 45 - chromium/content/browser/theme_helper_mac.mm | 44 +- .../browser/top_document_isolation_browsertest.cc | 571 ++++++ chromium/content/browser/tracing/BUILD.gn | 2 +- chromium/content/browser/tracing/DEPS | 1 + .../tracing/background_tracing_config_impl.cc | 8 + .../tracing/background_tracing_config_impl.h | 1 + .../tracing/background_tracing_config_unittest.cc | 38 + .../tracing/background_tracing_manager_impl.cc | 4 +- .../browser/tracing/battor_power_trace_provider.cc | 31 - .../browser/tracing/battor_power_trace_provider.h | 31 - .../tracing/etw_system_event_consumer_win.cc | 18 +- .../tracing/etw_system_event_consumer_win.h | 5 +- .../content/browser/tracing/power_tracing_agent.cc | 182 +- .../content/browser/tracing/power_tracing_agent.h | 61 +- .../browser/tracing/trace_message_filter.cc | 47 - .../content/browser/tracing/trace_message_filter.h | 8 - .../tracing/tracing_controller_browsertest.cc | 170 +- .../browser/tracing/tracing_controller_impl.cc | 494 ++--- .../browser/tracing/tracing_controller_impl.h | 55 +- .../tracing/tracing_controller_impl_data_sinks.cc | 7 +- chromium/content/browser/tracing/tracing_ui.cc | 77 - chromium/content/browser/tracing/tracing_ui.h | 1 - chromium/content/browser/udev_linux.cc | 66 - chromium/content/browser/udev_linux.h | 97 - .../content/browser/utility_process_host_impl.cc | 67 +- .../content/browser/utility_process_host_impl.h | 7 +- .../vr/android/cardboard/cardboard_vr_device.cc | 36 +- .../vr/android/cardboard/cardboard_vr_device.h | 4 +- chromium/content/browser/vr/vr_device.h | 4 +- chromium/content/browser/vr/vr_device_manager.cc | 9 +- chromium/content/browser/vr/vr_device_manager.h | 12 +- .../browser/vr/vr_device_manager_unittest.cc | 10 +- .../browser/wake_lock/wake_lock_browsertest.cc | 8 +- .../browser/wake_lock/wake_lock_service_context.cc | 6 +- .../browser/wake_lock/wake_lock_service_context.h | 2 +- .../browser/wake_lock/wake_lock_service_impl.cc | 2 +- .../browser/wake_lock/wake_lock_service_impl.h | 6 +- .../aura/overscroll_navigation_overlay_unittest.cc | 8 +- .../web_contents/aura/shadow_layer_delegate.cc | 8 +- .../browser/web_contents/web_contents_android.cc | 93 +- .../browser/web_contents/web_contents_android.h | 14 +- .../browser/web_contents/web_contents_impl.cc | 777 ++++---- .../browser/web_contents/web_contents_impl.h | 95 +- .../web_contents/web_contents_impl_browsertest.cc | 250 ++- .../web_contents/web_contents_impl_unittest.cc | 106 +- .../browser/web_contents/web_contents_view_aura.cc | 232 +-- .../browser/web_contents/web_contents_view_aura.h | 14 +- .../web_contents_view_aura_browsertest.cc | 2 +- .../browser/web_contents/web_contents_view_mac.h | 5 + .../browser/web_contents/web_contents_view_mac.mm | 69 +- .../browser/web_contents/web_contents_view_mus.cc | 1 - .../browser/web_contents/web_drag_dest_mac.mm | 4 +- .../web_contents/web_drag_dest_mac_unittest.mm | 76 +- .../browser/web_contents/web_drag_source_mac.mm | 8 +- .../web_contents/web_drag_source_mac_unittest.mm | 22 +- chromium/content/browser/webui/OWNERS | 1 + .../webui/content_web_ui_controller_factory.cc | 2 +- .../browser/webui/url_data_manager_backend.cc | 7 +- .../browser/webui/web_ui_data_source_impl.cc | 62 +- .../browser/webui/web_ui_data_source_impl.h | 12 +- chromium/content/browser/webui/web_ui_impl.h | 1 + .../browser/webui/web_ui_mojo_browsertest.cc | 34 +- .../zygote_host/zygote_communication_linux.cc | 437 +++++ .../zygote_host/zygote_communication_linux.h | 93 + .../browser/zygote_host/zygote_handle_linux.cc | 22 + .../browser/zygote_host/zygote_host_impl_linux.cc | 512 +---- .../browser/zygote_host/zygote_host_impl_linux.h | 89 +- chromium/content/child/BUILD.gn | 90 +- chromium/content/child/OWNERS | 3 + .../content/child/appcache/appcache_dispatcher.h | 4 +- .../child/appcache/appcache_frontend_impl.cc | 86 +- .../appcache/web_application_cache_host_impl.cc | 2 +- .../appcache/web_application_cache_host_impl.h | 2 +- chromium/content/child/assert_matching_enums.cc | 67 +- .../background_sync/background_sync_provider.cc | 306 +-- .../background_sync/background_sync_provider.h | 70 +- .../background_sync_type_converters.cc | 128 +- .../background_sync_type_converters.h | 78 +- .../background_sync_type_converters_unittest.cc | 170 +- chromium/content/child/blink_platform_impl.cc | 354 +--- chromium/content/child/blink_platform_impl.h | 71 +- .../child/blob_storage/blob_consolidation.cc | 4 + .../child/blob_storage/blob_consolidation.h | 10 +- .../child/blob_storage/blob_message_filter.cc | 61 + .../child/blob_storage/blob_message_filter.h | 63 + .../blob_storage/blob_transport_controller.cc | 119 +- .../child/blob_storage/blob_transport_controller.h | 58 +- .../blob_transport_controller_unittest.cc | 145 +- .../content/child/browser_font_resource_trusted.cc | 8 +- .../content/child/browser_font_resource_trusted.h | 4 +- .../child_discardable_shared_memory_manager.cc | 33 +- .../child_discardable_shared_memory_manager.h | 6 +- .../child/child_gpu_memory_buffer_manager.cc | 24 +- .../child/child_gpu_memory_buffer_manager.h | 7 +- .../child/child_histogram_message_filter.cc | 31 +- .../content/child/child_histogram_message_filter.h | 12 +- chromium/content/child/child_process.cc | 7 +- chromium/content/child/child_process.h | 9 +- .../content/child/child_shared_bitmap_manager.cc | 41 +- .../content/child/child_shared_bitmap_manager.h | 11 +- chromium/content/child/child_thread_impl.cc | 193 +- chromium/content/child/child_thread_impl.h | 49 +- .../content/child/child_thread_impl_browsertest.cc | 15 +- chromium/content/child/content_child_helpers.cc | 13 +- chromium/content/child/db_message_filter.cc | 23 +- .../dwrite_font_proxy_init_win.cc | 49 +- .../dwrite_font_proxy/dwrite_font_proxy_init_win.h | 6 +- .../dwrite_font_proxy/dwrite_font_proxy_win.cc | 58 +- .../dwrite_font_proxy/dwrite_font_proxy_win.h | 7 +- .../dwrite_font_proxy_win_unittest.cc | 6 +- .../content/child/fileapi/webfilesystem_impl.cc | 58 +- .../child/fileapi/webfilewriter_base_unittest.cc | 5 +- .../content/child/fileapi/webfilewriter_impl.cc | 2 +- chromium/content/child/font_warmup_win.cc | 435 +++++ chromium/content/child/font_warmup_win.h | 65 + chromium/content/child/font_warmup_win_unittest.cc | 435 +++++ .../ftp_directory_listing_response_delegate.cc | 9 +- .../child/geofencing/geofencing_dispatcher.cc | 3 +- .../child/geofencing/geofencing_dispatcher.h | 2 +- .../child/indexed_db/indexed_db_dispatcher.cc | 43 +- .../child/indexed_db/indexed_db_dispatcher.h | 20 +- .../indexed_db/indexed_db_dispatcher_unittest.cc | 25 +- .../content/child/indexed_db/webidbcursor_impl.cc | 4 +- .../child/indexed_db/webidbcursor_impl_unittest.cc | 13 +- .../content/child/indexed_db/webidbfactory_impl.cc | 26 +- .../content/child/indexed_db/webidbfactory_impl.h | 7 +- chromium/content/child/mojo/mojo_application.cc | 18 +- chromium/content/child/mojo/mojo_application.h | 2 - chromium/content/child/mojo/type_converters.h | 8 + .../content/child/multipart_response_delegate.cc | 394 ---- .../content/child/multipart_response_delegate.h | 153 -- .../child/multipart_response_delegate_unittest.cc | 680 ------- chromium/content/child/navigator_connect/OWNERS | 1 - .../service_port_dispatcher_impl.cc | 80 - .../service_port_dispatcher_impl.h | 57 - .../navigator_connect/service_port_provider.cc | 134 -- .../navigator_connect/service_port_provider.h | 101 - .../notifications/notification_data_conversions.cc | 38 +- .../notification_data_conversions_unittest.cc | 84 + .../notifications/notification_image_loader.cc | 41 +- .../notifications/notification_image_loader.h | 21 +- .../child/notifications/notification_manager.cc | 91 +- .../child/notifications/notification_manager.h | 11 +- .../child/notifications/pending_notification.cc | 123 ++ .../child/notifications/pending_notification.h | 78 + .../notifications/pending_notifications_tracker.cc | 105 +- .../notifications/pending_notifications_tracker.h | 96 +- .../pending_notifications_tracker_unittest.cc | 299 +++ chromium/content/child/npapi/DEPS | 4 - chromium/content/child/npapi/OWNERS | 2 - chromium/content/child/npapi/np_channel_base.cc | 393 ---- chromium/content/child/npapi/np_channel_base.h | 200 -- chromium/content/child/npapi/npobject_base.h | 31 - chromium/content/child/npapi/npobject_proxy.cc | 509 ----- chromium/content/child/npapi/npobject_proxy.h | 130 -- chromium/content/child/npapi/npobject_stub.cc | 424 ---- chromium/content/child/npapi/npobject_stub.h | 99 - chromium/content/child/npapi/npobject_util.cc | 310 --- chromium/content/child/npapi/npobject_util.h | 74 - chromium/content/child/npapi/npruntime_util.cc | 54 - chromium/content/child/npapi/npruntime_util.h | 24 - chromium/content/child/npapi/plugin_host.cc | 938 --------- chromium/content/child/npapi/plugin_host.h | 63 - chromium/content/child/npapi/plugin_instance.cc | 430 ----- chromium/content/child/npapi/plugin_instance.h | 293 --- .../content/child/npapi/plugin_instance_mac.mm | 56 - chromium/content/child/npapi/plugin_lib.cc | 333 ---- chromium/content/child/npapi/plugin_lib.h | 135 -- .../content/child/npapi/plugin_lib_unittest.cc | 64 - .../child/npapi/plugin_web_event_converter_mac.h | 55 - .../child/npapi/plugin_web_event_converter_mac.mm | 189 -- chromium/content/child/npapi/webplugin.h | 133 -- .../npapi/webplugin_accelerated_surface_mac.h | 39 - chromium/content/child/npapi/webplugin_delegate.h | 96 - .../content/child/npapi/webplugin_delegate_impl.cc | 242 --- .../content/child/npapi/webplugin_delegate_impl.h | 449 ----- .../child/npapi/webplugin_delegate_impl_android.cc | 80 - .../child/npapi/webplugin_delegate_impl_aura.cc | 66 - .../child/npapi/webplugin_delegate_impl_mac.mm | 726 ------- .../child/npapi/webplugin_delegate_impl_win.cc | 1496 -------------- chromium/content/child/npapi/webplugin_ime_win.cc | 327 ---- chromium/content/child/npapi/webplugin_ime_win.h | 184 -- .../child/npapi/webplugin_resource_client.h | 45 - .../child/permissions/permission_dispatcher.cc | 107 +- .../child/permissions/permission_dispatcher.h | 34 +- chromium/content/child/plugin_message_generator.cc | 33 - chromium/content/child/plugin_message_generator.h | 7 - chromium/content/child/plugin_messages.h | 346 ---- chromium/content/child/plugin_param_traits.cc | 136 -- chromium/content/child/plugin_param_traits.h | 87 - .../power_monitor_broadcast_source_unittest.cc | 4 +- chromium/content/child/process_control_impl.cc | 20 +- chromium/content/child/process_control_impl.h | 26 +- .../content/child/push_messaging/push_provider.cc | 20 +- chromium/content/child/request_extra_data.cc | 1 + chromium/content/child/request_extra_data.h | 15 +- chromium/content/child/request_info.h | 7 +- chromium/content/child/resource_dispatcher.cc | 192 +- chromium/content/child/resource_dispatcher.h | 44 +- .../content/child/resource_dispatcher_unittest.cc | 546 +++--- .../content/child/resource_scheduling_filter.cc | 3 +- .../content/child/resource_scheduling_filter.h | 6 +- chromium/content/child/runtime_features.cc | 88 +- chromium/content/child/scoped_web_callbacks.h | 17 +- .../service_worker/service_worker_dispatcher.cc | 53 +- .../service_worker/service_worker_dispatcher.h | 7 +- .../service_worker_dispatcher_unittest.cc | 17 +- .../service_worker_handle_reference.cc | 19 +- .../service_worker_handle_reference.h | 7 +- .../service_worker_network_provider.cc | 12 +- .../service_worker_network_provider.h | 7 +- .../service_worker_provider_context.cc | 52 +- .../service_worker_provider_context.h | 13 +- ...service_worker_registration_handle_reference.cc | 13 +- .../service_worker_registration_handle_reference.h | 7 +- .../service_worker/web_service_worker_impl.cc | 54 +- .../child/service_worker/web_service_worker_impl.h | 12 +- .../web_service_worker_registration_impl.cc | 10 +- .../web_service_worker_registration_impl.h | 8 +- .../child/shared_memory_data_consumer_handle.cc | 28 +- .../child/shared_memory_data_consumer_handle.h | 11 +- .../shared_memory_data_consumer_handle_unittest.cc | 66 +- .../child/shared_memory_received_data_factory.cc | 11 +- .../child/shared_memory_received_data_factory.h | 8 +- ...shared_memory_received_data_factory_unittest.cc | 40 +- .../content/child/shared_worker_devtools_agent.cc | 1 - .../content/child/simple_webmimeregistry_impl.cc | 12 +- .../content/child/simple_webmimeregistry_impl.h | 1 - .../content/child/site_isolation_stats_gatherer.cc | 8 +- .../content/child/site_isolation_stats_gatherer.h | 6 +- chromium/content/child/storage_util.cc | 26 + chromium/content/child/storage_util.h | 24 + chromium/content/child/threaded_data_provider.cc | 349 ---- chromium/content/child/threaded_data_provider.h | 106 - chromium/content/child/v8_value_converter_impl.cc | 43 +- .../child/v8_value_converter_impl_unittest.cc | 310 ++- .../content/child/web_data_consumer_handle_impl.cc | 6 +- .../content/child/web_data_consumer_handle_impl.h | 5 +- .../web_data_consumer_handle_impl_unittest.cc | 20 +- .../content/child/web_database_observer_impl.cc | 97 +- .../content/child/web_database_observer_impl.h | 20 +- .../content/child/web_discardable_memory_impl.cc | 50 - .../content/child/web_discardable_memory_impl.h | 46 - .../child/web_memory_allocator_dump_impl.cc | 41 - .../content/child/web_memory_allocator_dump_impl.h | 48 - .../child/web_memory_dump_provider_adapter.cc | 131 -- .../child/web_memory_dump_provider_adapter.h | 49 - .../content/child/web_process_memory_dump_impl.cc | 165 -- .../content/child/web_process_memory_dump_impl.h | 111 -- .../child/web_process_memory_dump_impl_unittest.cc | 124 -- chromium/content/child/web_url_loader_impl.cc | 429 ++-- chromium/content/child/web_url_loader_impl.h | 11 +- .../content/child/web_url_loader_impl_unittest.cc | 245 +-- chromium/content/child/web_url_request_util.cc | 262 ++- chromium/content/child/webblobregistry_impl.cc | 150 +- chromium/content/child/webblobregistry_impl.h | 29 +- .../content/child/webfallbackthemeengine_impl.h | 5 +- chromium/content/child/webfileutilities_impl.cc | 9 +- .../content/child/webmessageportchannel_impl.cc | 22 +- .../content/child/webmessageportchannel_impl.h | 12 +- .../content/child/webthemeengine_impl_android.cc | 29 +- .../content/child/webthemeengine_impl_android.h | 2 + chromium/content/child/worker_thread_registry.cc | 3 +- chromium/content/common/BUILD.gn | 176 +- chromium/content/common/DEPS | 9 +- chromium/content/common/OWNERS | 13 + ...celerated_surface_buffers_swapped_params_mac.cc | 14 + ...ccelerated_surface_buffers_swapped_params_mac.h | 28 + chromium/content/common/accessibility_messages.h | 21 +- .../common/android/address_parser_internal.cc | 2 + .../common/android/address_parser_internal.h | 1 + .../common/android/media_metadata_android.cc | 33 + .../common/android/media_metadata_android.h | 29 + .../common/android/surface_texture_manager.cc | 28 - .../common/android/surface_texture_manager.h | 42 - .../content/common/android/surface_texture_peer.cc | 33 - .../content/common/android/surface_texture_peer.h | 38 - .../common/android/sync_compositor_messages.cc | 5 +- .../common/android/sync_compositor_messages.h | 33 +- chromium/content/common/appcache_interfaces.cc | 5 + chromium/content/common/appcache_interfaces.h | 1 + chromium/content/common/appcache_messages.h | 1 + chromium/content/common/application_setup.mojom | 9 +- chromium/content/common/ax_content_node_data.cc | 2 + chromium/content/common/ax_content_node_data.h | 1 + .../content/common/background_sync_service.mojom | 30 +- .../content/common/bluetooth/bluetooth_messages.h | 59 +- .../common/bluetooth/bluetooth_scan_filter.cc | 3 + .../common/bluetooth/bluetooth_scan_filter.h | 1 + .../browser_plugin/browser_plugin_messages.h | 23 +- .../content/common/buffer_presented_params_mac.cc | 13 + .../content/common/buffer_presented_params_mac.h | 23 + .../common/cache_storage/cache_storage_messages.h | 12 +- .../common/cache_storage/cache_storage_types.cc | 3 + .../common/cache_storage/cache_storage_types.h | 5 +- chromium/content/common/cc_messages.cc | 124 +- chromium/content/common/cc_messages.h | 73 +- chromium/content/common/cc_messages_unittest.cc | 12 +- chromium/content/common/child_process_host_impl.cc | 22 +- chromium/content/common/child_process_messages.h | 57 +- .../child_process_sandbox_support_impl_linux.h | 5 +- chromium/content/common/clipboard_messages.h | 5 +- chromium/content/common/common.sb | 7 - .../content/common/common_param_traits_unittest.cc | 63 +- .../content/common/content_constants_internal.cc | 5 - .../content/common/content_constants_internal.h | 7 - .../content/common/content_message_generator.h | 7 +- chromium/content/common/content_param_traits.cc | 5 +- chromium/content/common/content_param_traits.h | 14 +- .../content/common/content_param_traits_macros.h | 3 + .../content/common/content_switches_internal.cc | 37 +- .../content/common/content_switches_internal.h | 6 +- .../content/common/database_identifier_unittest.cc | 38 + chromium/content/common/devtools_messages.h | 14 +- .../common/discardable_shared_memory_heap.cc | 4 +- .../common/dom_storage/dom_storage_messages.h | 2 +- chromium/content/common/drag_messages.h | 1 - .../content/common/dwrite_font_platform_win.cc | 1282 ------------ .../common/dwrite_font_platform_win_unittest.cc | 71 - .../content/common/establish_channel_params.cc | 18 + chromium/content/common/establish_channel_params.h | 27 + chromium/content/common/experiments/api_key.cc | 144 -- chromium/content/common/experiments/api_key.h | 95 - .../content/common/experiments/api_key_unittest.cc | 191 -- chromium/content/common/fileapi/webblob_messages.h | 80 +- chromium/content/common/font_config_ipc_linux.cc | 16 +- chromium/content/common/font_config_ipc_linux.h | 16 +- chromium/content/common/font_list_win.cc | 2 +- chromium/content/common/font_warmup_win.cc | 526 ----- chromium/content/common/font_warmup_win.h | 76 - .../content/common/font_warmup_win_unittest.cc | 435 ----- chromium/content/common/frame_message_enums.h | 6 +- chromium/content/common/frame_messages.h | 225 ++- chromium/content/common/frame_param.cc | 40 - chromium/content/common/frame_param.h | 10 - chromium/content/common/frame_param_macros.h | 60 - chromium/content/common/frame_replication_state.cc | 15 +- chromium/content/common/frame_replication_state.h | 45 +- chromium/content/common/gamepad_param_traits.cc | 6 +- chromium/content/common/gamepad_param_traits.h | 5 +- chromium/content/common/geolocation_service.mojom | 20 - chromium/content/common/gpu/DEPS | 3 - .../common/gpu/ca_layer_partial_damage_tree_mac.h | 56 - .../common/gpu/ca_layer_partial_damage_tree_mac.mm | 287 --- chromium/content/common/gpu/ca_layer_tree_mac.h | 190 -- chromium/content/common/gpu/ca_layer_tree_mac.mm | 455 ----- .../content/common/gpu/child_window_surface_win.cc | 214 -- .../content/common/gpu/child_window_surface_win.h | 45 - chromium/content/common/gpu/client/DEPS | 7 - .../common/gpu/client/command_buffer_metrics.cc | 6 +- .../common/gpu/client/command_buffer_metrics.h | 2 +- .../common/gpu/client/command_buffer_proxy_impl.cc | 820 -------- .../common/gpu/client/command_buffer_proxy_impl.h | 279 --- .../gpu/client/context_provider_command_buffer.cc | 63 +- .../gpu/client/context_provider_command_buffer.h | 12 +- chromium/content/common/gpu/client/gl_helper.cc | 1391 ------------- chromium/content/common/gpu/client/gl_helper.h | 382 ---- .../common/gpu/client/gl_helper_benchmark.cc | 310 --- .../gpu/client/gl_helper_readback_support.cc | 183 -- .../common/gpu/client/gl_helper_readback_support.h | 72 - .../content/common/gpu/client/gl_helper_scaling.cc | 934 --------- .../content/common/gpu/client/gl_helper_scaling.h | 213 -- .../common/gpu/client/gl_helper_unittest.cc | 2016 ------------------- .../content/common/gpu/client/gpu_channel_host.cc | 552 ------ .../content/common/gpu/client/gpu_channel_host.h | 318 --- .../content/common/gpu/client/gpu_context_tests.h | 72 +- .../gpu/client/gpu_in_process_context_tests.cc | 42 +- .../gpu/client/gpu_jpeg_decode_accelerator_host.cc | 204 -- .../gpu/client/gpu_jpeg_decode_accelerator_host.h | 73 - .../common/gpu/client/gpu_memory_buffer_impl.cc | 94 - .../common/gpu/client/gpu_memory_buffer_impl.h | 68 - .../client/gpu_memory_buffer_impl_io_surface.cc | 125 -- .../gpu/client/gpu_memory_buffer_impl_io_surface.h | 63 - .../gpu_memory_buffer_impl_io_surface_unittest.cc | 16 - .../gpu_memory_buffer_impl_ozone_native_pixmap.cc | 112 -- .../gpu_memory_buffer_impl_ozone_native_pixmap.h | 63 - ...ory_buffer_impl_ozone_native_pixmap_unittest.cc | 16 - .../client/gpu_memory_buffer_impl_shared_memory.cc | 224 --- .../client/gpu_memory_buffer_impl_shared_memory.h | 77 - ...pu_memory_buffer_impl_shared_memory_unittest.cc | 40 - .../gpu_memory_buffer_impl_surface_texture.cc | 151 -- .../gpu_memory_buffer_impl_surface_texture.h | 60 - ..._memory_buffer_impl_surface_texture_unittest.cc | 8 +- .../client/gpu_video_decode_accelerator_host.cc | 292 --- .../gpu/client/gpu_video_decode_accelerator_host.h | 106 - .../client/gpu_video_encode_accelerator_host.cc | 323 ---- .../gpu/client/gpu_video_encode_accelerator_host.h | 131 -- .../gpu/client/grcontext_for_gles2_interface.cc | 62 + .../gpu/client/grcontext_for_gles2_interface.h | 42 + .../client/grcontext_for_webgraphicscontext3d.cc | 108 -- .../client/grcontext_for_webgraphicscontext3d.h | 66 - .../webgraphicscontext3d_command_buffer_impl.cc | 126 +- .../webgraphicscontext3d_command_buffer_impl.h | 53 +- chromium/content/common/gpu/gpu_channel.cc | 1086 ----------- chromium/content/common/gpu/gpu_channel.h | 485 ----- chromium/content/common/gpu/gpu_channel_manager.cc | 380 ---- chromium/content/common/gpu/gpu_channel_manager.h | 227 --- .../common/gpu/gpu_channel_manager_unittest.cc | 120 -- .../content/common/gpu/gpu_channel_test_common.cc | 112 -- .../content/common/gpu/gpu_channel_test_common.h | 93 - .../content/common/gpu/gpu_channel_unittest.cc | 329 ---- .../content/common/gpu/gpu_command_buffer_stub.cc | 1269 ------------ .../content/common/gpu/gpu_command_buffer_stub.h | 307 --- chromium/content/common/gpu/gpu_config.h | 12 - .../common/gpu/gpu_memory_buffer_factory.cc | 53 - .../content/common/gpu/gpu_memory_buffer_factory.h | 71 - .../gpu/gpu_memory_buffer_factory_io_surface.cc | 121 -- .../gpu/gpu_memory_buffer_factory_io_surface.h | 80 - ...pu_memory_buffer_factory_io_surface_unittest.cc | 16 - ...pu_memory_buffer_factory_ozone_native_pixmap.cc | 139 -- ...gpu_memory_buffer_factory_ozone_native_pixmap.h | 70 - ..._buffer_factory_ozone_native_pixmap_unittest.cc | 20 - .../gpu_memory_buffer_factory_surface_texture.cc | 124 -- .../gpu_memory_buffer_factory_surface_texture.h | 75 - ...mory_buffer_factory_surface_texture_unittest.cc | 16 - chromium/content/common/gpu/gpu_memory_manager.cc | 124 -- chromium/content/common/gpu/gpu_memory_manager.h | 78 - chromium/content/common/gpu/gpu_memory_tracking.cc | 37 - chromium/content/common/gpu/gpu_memory_tracking.h | 53 - chromium/content/common/gpu/gpu_memory_uma_stats.h | 33 - chromium/content/common/gpu/gpu_messages.h | 878 --------- .../content/common/gpu/gpu_process_launch_causes.h | 35 - chromium/content/common/gpu/gpu_result_codes.h | 20 - chromium/content/common/gpu/gpu_stream_priority.h | 20 - chromium/content/common/gpu/gpu_surface_lookup.cc | 33 - chromium/content/common/gpu/gpu_surface_lookup.h | 40 - chromium/content/common/gpu/gpu_watchdog.h | 28 - .../content/common/gpu/image_transport_surface.cc | 302 --- .../content/common/gpu/image_transport_surface.h | 224 --- .../common/gpu/image_transport_surface_android.cc | 42 - .../common/gpu/image_transport_surface_linux.cc | 28 - .../common/gpu/image_transport_surface_mac.mm | 82 - .../gpu/image_transport_surface_overlay_mac.h | 148 -- .../gpu/image_transport_surface_overlay_mac.mm | 522 ----- .../common/gpu/image_transport_surface_win.cc | 53 - chromium/content/common/gpu/media/OWNERS | 13 + .../gpu/media/android_copying_backing_strategy.cc | 113 +- .../gpu/media/android_copying_backing_strategy.h | 10 +- .../android_deferred_rendering_backing_strategy.cc | 329 +++- .../android_deferred_rendering_backing_strategy.h | 40 +- .../gpu/media/android_video_decode_accelerator.cc | 897 ++++++--- .../gpu/media/android_video_decode_accelerator.h | 206 +- .../android_video_decode_accelerator_unittest.cc | 23 +- .../gpu/media/android_video_encode_accelerator.cc | 86 +- .../gpu/media/android_video_encode_accelerator.h | 9 +- .../content/common/gpu/media/avda_codec_image.cc | 154 +- .../content/common/gpu/media/avda_codec_image.h | 60 +- .../content/common/gpu/media/avda_shared_state.cc | 14 +- .../content/common/gpu/media/avda_shared_state.h | 11 +- .../content/common/gpu/media/avda_state_provider.h | 3 + .../gpu/media/dxva_video_decode_accelerator_win.cc | 1017 ++++++++-- .../gpu/media/dxva_video_decode_accelerator_win.h | 118 +- .../gpu/media/fake_video_decode_accelerator.cc | 35 +- .../gpu/media/fake_video_decode_accelerator.h | 14 +- .../common/gpu/media/gpu_arc_video_service.cc | 92 - .../common/gpu/media/gpu_arc_video_service.h | 68 - .../gpu/media/gpu_jpeg_decode_accelerator.cc | 56 +- .../common/gpu/media/gpu_jpeg_decode_accelerator.h | 12 +- .../common/gpu/media/gpu_video_accelerator_util.cc | 155 -- .../common/gpu/media/gpu_video_accelerator_util.h | 63 - .../gpu/media/gpu_video_decode_accelerator.cc | 446 ++--- .../gpu/media/gpu_video_decode_accelerator.h | 71 +- .../gpu_video_decode_accelerator_factory_impl.cc | 242 +++ .../gpu_video_decode_accelerator_factory_impl.h | 123 ++ .../media/gpu_video_decode_accelerator_helpers.h | 59 + .../gpu/media/gpu_video_encode_accelerator.cc | 207 +- .../gpu/media/gpu_video_encode_accelerator.h | 41 +- chromium/content/common/gpu/media/media_channel.cc | 145 ++ chromium/content/common/gpu/media/media_channel.h | 57 + chromium/content/common/gpu/media/media_service.cc | 40 + chromium/content/common/gpu/media/media_service.h | 42 + .../content/common/gpu/media/rendering_helper.cc | 14 +- .../content/common/gpu/media/rendering_helper.h | 7 +- .../common/gpu/media/shared_memory_region.cc | 42 + .../common/gpu/media/shared_memory_region.h | 57 + .../common/gpu/media/v4l2_image_processor.cc | 30 +- .../gpu/media/v4l2_jpeg_decode_accelerator.cc | 45 +- .../gpu/media/v4l2_jpeg_decode_accelerator.h | 11 +- .../media/v4l2_slice_video_decode_accelerator.cc | 98 +- .../media/v4l2_slice_video_decode_accelerator.h | 29 +- .../gpu/media/v4l2_video_decode_accelerator.cc | 202 +- .../gpu/media/v4l2_video_decode_accelerator.h | 31 +- .../gpu/media/v4l2_video_encode_accelerator.cc | 103 +- .../content/common/gpu/media/vaapi_drm_picture.cc | 8 +- .../content/common/gpu/media/vaapi_drm_picture.h | 5 +- .../gpu/media/vaapi_jpeg_decode_accelerator.cc | 39 +- .../gpu/media/vaapi_jpeg_decode_accelerator.h | 9 +- chromium/content/common/gpu/media/vaapi_picture.cc | 6 +- chromium/content/common/gpu/media/vaapi_picture.h | 6 +- .../content/common/gpu/media/vaapi_tfp_picture.cc | 8 +- .../content/common/gpu/media/vaapi_tfp_picture.h | 5 +- .../gpu/media/vaapi_video_decode_accelerator.cc | 63 +- .../gpu/media/vaapi_video_decode_accelerator.h | 30 +- .../gpu/media/vaapi_video_encode_accelerator.cc | 40 +- chromium/content/common/gpu/media/vaapi_wrapper.cc | 24 +- chromium/content/common/gpu/media/vaapi_wrapper.h | 2 +- .../gpu/media/video_decode_accelerator_unittest.cc | 187 +- .../gpu/media/video_encode_accelerator_unittest.cc | 58 +- .../gpu/media/vt_video_decode_accelerator_mac.cc | 135 +- .../gpu/media/vt_video_decode_accelerator_mac.h | 19 +- .../gpu/media/vt_video_encode_accelerator_mac.cc | 552 ++++++ .../gpu/media/vt_video_encode_accelerator_mac.h | 142 ++ .../content/common/gpu/stream_texture_android.cc | 386 ---- .../content/common/gpu/stream_texture_android.h | 110 -- chromium/content/common/gpu/x_util.h | 40 - chromium/content/common/gpu_host_messages.h | 300 +++ .../content/common/gpu_process_launch_causes.h | 35 + .../host_discardable_shared_memory_manager.cc | 8 +- .../host_discardable_shared_memory_manager.h | 1 + .../content/common/host_shared_bitmap_manager.cc | 5 - chromium/content/common/id_type.h | 112 -- chromium/content/common/id_type_unittest.cc | 205 -- .../common/image_downloader/image_downloader.mojom | 23 +- .../common/in_process_child_thread_params.cc | 9 +- .../common/in_process_child_thread_params.h | 7 +- .../common/indexed_db/indexed_db_messages.h | 17 +- .../common/indexed_db/indexed_db_param_traits.cc | 14 +- .../common/indexed_db/indexed_db_param_traits.h | 18 +- .../content/common/input/event_with_latency_info.h | 63 + .../input/event_with_latency_info_unittest.cc | 122 ++ .../content/common/input/input_event_ack_state.h | 3 +- .../common/input/input_event_dispatch_type.h | 26 + chromium/content/common/input/input_event_utils.cc | 17 + chromium/content/common/input/input_event_utils.h | 17 + .../content/common/input/input_param_traits.cc | 24 +- chromium/content/common/input/input_param_traits.h | 12 +- .../common/input/input_param_traits_unittest.cc | 60 + .../common/input/synthetic_gesture_params.h | 4 +- .../input/synthetic_pointer_action_params.cc | 48 + .../common/input/synthetic_pointer_action_params.h | 86 + .../input/synthetic_web_input_event_builders.cc | 17 +- .../input/synthetic_web_input_event_builders.h | 8 + .../content/common/input/web_input_event_queue.h | 79 + .../content/common/input/web_input_event_traits.cc | 45 +- .../content/common/input/web_input_event_traits.h | 2 +- .../input/web_input_event_traits_unittest.cc | 28 + .../content/common/input/web_touch_event_traits.cc | 4 +- chromium/content/common/input_messages.h | 27 +- chromium/content/common/leveldb_wrapper.mojom | 51 + .../content/common/mac/attributed_string_coder.h | 17 +- .../content/common/mac/attributed_string_coder.mm | 10 +- chromium/content/common/media/OWNERS | 6 +- chromium/content/common/media/audio_messages.h | 1 + .../content/common/media/media_param_traits.cc | 9 +- chromium/content/common/media/media_param_traits.h | 12 +- .../common/media/media_player_delegate_messages.h | 51 + .../common/media/media_player_messages_android.h | 8 + .../common/media/media_session_messages_android.h | 11 + .../content/common/media/media_stream_options.cc | 2 + .../content/common/media/media_stream_options.h | 1 + .../media/surface_view_manager_messages_android.h | 26 + .../content/common/media/video_capture_messages.h | 6 +- .../content/common/memory_benchmark_messages.h | 15 - chromium/content/common/message_router.cc | 57 - chromium/content/common/message_router.h | 71 - chromium/content/common/mojo/DEPS | 5 +- chromium/content/common/mojo/channel_init.cc | 46 +- chromium/content/common/mojo/channel_init.h | 26 +- .../content/common/mojo/current_thread_loader.cc | 25 + .../content/common/mojo/current_thread_loader.h | 46 + chromium/content/common/mojo/mojo_messages.h | 5 - .../common/mojo/mojo_shell_connection_impl.cc | 120 +- .../common/mojo/mojo_shell_connection_impl.h | 56 +- .../content/common/mojo/service_registry_impl.cc | 35 +- .../content/common/mojo/service_registry_impl.h | 33 +- chromium/content/common/mojo/static_loader.cc | 93 + chromium/content/common/mojo/static_loader.h | 66 + chromium/content/common/navigation_params.cc | 41 +- chromium/content/common/navigation_params.h | 20 +- .../common/net/url_request_service_worker_data.cc | 17 + .../common/net/url_request_service_worker_data.h | 28 + chromium/content/common/notification_constants.h | 12 + .../content/common/one_writer_seqlock_unittest.cc | 8 +- .../content/common/origin_trials/trial_token.cc | 193 ++ .../content/common/origin_trials/trial_token.h | 90 + .../common/origin_trials/trial_token_unittest.cc | 300 +++ .../common/origin_trials/trial_token_validator.cc | 30 + .../common/origin_trials/trial_token_validator.h | 26 + .../trial_token_validator_unittest.cc | 160 ++ chromium/content/common/origin_util.cc | 2 +- chromium/content/common/p2p_messages.h | 5 +- chromium/content/common/page_messages.h | 25 + .../content/common/page_state_serialization.cc | 6 +- chromium/content/common/page_state_serialization.h | 1 + chromium/content/common/pepper_plugin_list.cc | 13 +- chromium/content/common/permission_service.mojom | 42 - .../common/platform_notification_messages.h | 42 +- chromium/content/common/plugin_constants_win.cc | 29 - chromium/content/common/plugin_constants_win.h | 52 - chromium/content/common/plugin_list.cc | 160 +- chromium/content/common/plugin_list.h | 72 +- chromium/content/common/plugin_list_mac.mm | 307 --- chromium/content/common/plugin_list_posix.cc | 37 - chromium/content/common/plugin_list_unittest.cc | 8 +- chromium/content/common/plugin_list_win.cc | 493 ----- chromium/content/common/plugin_process_messages.h | 87 - .../common/presentation/presentation_service.mojom | 14 +- chromium/content/common/process_control.mojom | 7 +- chromium/content/common/process_type.cc | 2 - chromium/content/common/push_messaging_messages.h | 13 +- chromium/content/common/render_frame_setup.mojom | 11 +- chromium/content/common/resize_params.cc | 20 + chromium/content/common/resize_params.h | 58 + chromium/content/common/resource_messages.cc | 31 +- chromium/content/common/resource_messages.h | 55 +- chromium/content/common/sandbox_init_mac.cc | 11 +- chromium/content/common/sandbox_init_win.cc | 15 - .../android/sandbox_bpf_base_policy_android.cc | 85 + .../sandbox_linux/bpf_renderer_policy_linux.cc | 23 +- .../sandbox_linux/bpf_utility_policy_linux.cc | 3 +- .../content/common/sandbox_linux/sandbox_linux.h | 2 +- chromium/content/common/sandbox_mac.h | 3 - chromium/content/common/sandbox_mac.mm | 87 +- .../common/sandbox_mac_diraccess_unittest.mm | 1 + .../common/sandbox_mac_system_access_unittest.mm | 11 +- chromium/content/common/sandbox_util.cc | 26 +- chromium/content/common/sandbox_win.cc | 66 +- chromium/content/common/service_port_service.mojom | 37 - .../content/common/service_port_type_converters.cc | 32 - .../content/common/service_port_type_converters.h | 31 - .../service_worker/embedded_worker_messages.h | 15 +- .../service_worker/embedded_worker_settings.h | 19 + .../service_worker/embedded_worker_setup.mojom | 11 +- .../service_worker/service_worker_client_info.cc | 23 +- .../service_worker/service_worker_client_info.h | 6 +- .../service_worker/service_worker_messages.h | 94 +- .../service_worker/service_worker_status_code.h | 51 +- .../service_worker_type_converters.cc | 10 +- .../service_worker_type_converters.h | 4 +- .../common/service_worker/service_worker_types.cc | 40 +- .../common/service_worker/service_worker_types.h | 39 +- .../common/service_worker/service_worker_utils.cc | 20 +- .../common/service_worker/service_worker_utils.h | 4 + .../service_worker_utils_unittest.cc | 2 + chromium/content/common/site_isolation_policy.cc | 23 +- chromium/content/common/site_isolation_policy.h | 15 +- .../content/common/storage_partition_service.mojom | 15 + chromium/content/common/swapped_out_messages.cc | 7 +- .../content/common/text_input_client_messages.h | 2 +- chromium/content/common/url_schemes.cc | 34 +- chromium/content/common/url_schemes.h | 12 +- chromium/content/common/utility_messages.h | 32 - chromium/content/common/view_messages.h | 188 +- chromium/content/common/vr_service.mojom | 2 +- chromium/content/common/wake_lock_service.mojom | 2 +- chromium/content/common/webplugin_geometry.cc | 27 - chromium/content/common/webplugin_geometry.h | 46 - chromium/content/common/websocket_messages.h | 30 + chromium/content/common/worker_messages.h | 11 +- chromium/content/content.gni | 12 - chromium/content/content.gyp | 292 +-- chromium/content/content_app.gypi | 15 +- chromium/content/content_browser.gypi | 412 ++-- chromium/content/content_browsertests.isolate | 3 - chromium/content/content_browsertests_apk.isolate | 2 + chromium/content/content_child.gypi | 115 +- chromium/content/content_common.gypi | 355 ++-- chromium/content/content_common_mojo_bindings.gyp | 36 +- chromium/content/content_gpu.gypi | 7 + chromium/content/content_jni.gypi | 3 +- chromium/content/content_plugin.gypi | 46 - chromium/content/content_ppapi_plugin.gypi | 1 + chromium/content/content_renderer.gypi | 73 +- chromium/content/content_resources.grd | 3 + chromium/content/content_resources.gypi | 8 + chromium/content/content_shell.gypi | 195 +- chromium/content/content_shell_and_tests.gyp | 10 +- chromium/content/content_shell_test_apk.isolate | 1 + .../content/content_shell_test_apk_run.isolate | 4 +- .../content_site_isolation_browsertests.isolate | 21 + chromium/content/content_tests.gypi | 1442 +++++++------- chromium/content/content_unittests_apk.isolate | 1 + chromium/content/content_utility.gypi | 4 +- chromium/content/gpu/BUILD.gn | 36 +- chromium/content/gpu/DEPS | 2 + chromium/content/gpu/gpu_child_thread.cc | 249 ++- chromium/content/gpu/gpu_child_thread.h | 105 +- chromium/content/gpu/gpu_main.cc | 45 +- chromium/content/gpu/gpu_process.cc | 4 +- chromium/content/gpu/gpu_process.h | 2 +- chromium/content/gpu/gpu_process_control_impl.cc | 24 +- chromium/content/gpu/gpu_process_control_impl.h | 2 +- chromium/content/gpu/gpu_watchdog_thread.cc | 55 +- chromium/content/gpu/gpu_watchdog_thread.h | 25 +- chromium/content/gpu/in_process_gpu_thread.cc | 28 +- chromium/content/gpu/in_process_gpu_thread.h | 13 +- chromium/content/plugin/BUILD.gn | 46 - chromium/content/plugin/DEPS | 9 - chromium/content/plugin/OWNERS | 3 - chromium/content/plugin/plugin_channel.cc | 331 ---- chromium/content/plugin/plugin_channel.h | 111 -- .../content/plugin/plugin_interpose_util_mac.h | 16 - .../content/plugin/plugin_interpose_util_mac.mm | 261 --- chromium/content/plugin/plugin_main.cc | 71 - chromium/content/plugin/plugin_main_mac.mm | 16 - chromium/content/plugin/plugin_thread.cc | 160 -- chromium/content/plugin/plugin_thread.h | 65 - .../webplugin_accelerated_surface_proxy_mac.cc | 83 - .../webplugin_accelerated_surface_proxy_mac.h | 49 - chromium/content/plugin/webplugin_delegate_stub.cc | 352 ---- chromium/content/plugin/webplugin_delegate_stub.h | 117 -- chromium/content/plugin/webplugin_proxy.cc | 566 ------ chromium/content/plugin/webplugin_proxy.h | 212 -- chromium/content/ppapi_plugin/BUILD.gn | 14 +- chromium/content/ppapi_plugin/DEPS | 3 +- .../ppapi_plugin/ppapi_blink_platform_impl.cc | 8 +- chromium/content/ppapi_plugin/ppapi_plugin_main.cc | 26 +- chromium/content/ppapi_plugin/ppapi_thread.cc | 36 +- chromium/content/public/DEPS | 2 + chromium/content/public/android/BUILD.gn | 214 +- chromium/content/public/app/BUILD.gn | 16 + chromium/content/public/app/DEPS | 2 +- .../content/public/app/content_main_delegate.cc | 24 +- .../content/public/app/content_main_delegate.h | 8 +- .../public/app/mojo/content_browser_manifest.json | 14 + .../public/app/mojo/content_renderer_manifest.json | 5 + chromium/content/public/browser/BUILD.gn | 43 +- .../content/public/browser/access_token_store.h | 10 +- .../content/public/browser/android/compositor.h | 4 - .../browser/android/download_controller_android.h | 16 +- .../browser/android/service_registry_android.h | 51 + .../browser/android/synchronous_compositor.cc | 27 + .../browser/android/synchronous_compositor.h | 26 +- .../public/browser/arc_video_host_delegate.h | 21 - .../browser/ax_event_notification_details.cc | 3 + .../public/browser/ax_event_notification_details.h | 1 + .../public/browser/background_tracing_config.h | 1 + .../public/browser/background_tracing_manager.h | 1 + .../content/public/browser/bluetooth_chooser.h | 8 +- .../public/browser/browser_child_process_host.h | 4 +- .../browser/browser_child_process_observer.h | 6 - chromium/content/public/browser/browser_context.h | 48 +- .../public/browser/browser_message_filter.cc | 30 - .../public/browser/browser_message_filter.h | 5 - .../public/browser/content_browser_client.cc | 51 +- .../public/browser/content_browser_client.h | 98 +- .../content/public/browser/cookie_store_factory.h | 9 +- .../content/public/browser/desktop_media_id.cc | 20 +- chromium/content/public/browser/desktop_media_id.h | 6 + .../public/browser/download_destination_observer.h | 39 - .../browser/download_interrupt_reason_values.h | 7 + chromium/content/public/browser/download_item.h | 22 +- chromium/content/public/browser/download_manager.h | 32 +- .../content/public/browser/download_save_info.cc | 9 + .../content/public/browser/download_save_info.h | 14 +- .../public/browser/download_url_parameters.h | 133 +- .../content/public/browser/geolocation_provider.h | 6 +- chromium/content/public/browser/gpu_data_manager.h | 4 - .../public/browser/gpu_data_manager_observer.h | 7 +- .../content/public/browser/gpu_service_registry.cc | 18 + .../content/public/browser/gpu_service_registry.h | 19 + chromium/content/public/browser/gpu_utils.cc | 103 + chromium/content/public/browser/gpu_utils.h | 17 + chromium/content/public/browser/host_zoom_map.h | 1 + .../content/public/browser/indexed_db_context.h | 3 +- .../public/browser/javascript_dialog_manager.h | 2 - .../public/browser/memory_pressure_controller.cc | 27 + .../public/browser/memory_pressure_controller.h | 40 + .../content/public/browser/message_port_delegate.h | 5 - .../content/public/browser/mojo_app_connection.h | 23 +- .../public/browser/native_web_keyboard_event.h | 24 +- .../content/public/browser/navigation_controller.h | 26 +- .../content/public/browser/navigation_details.cc | 3 + .../content/public/browser/navigation_details.h | 1 + chromium/content/public/browser/navigation_entry.h | 7 +- .../content/public/browser/navigation_handle.cc | 4 +- .../content/public/browser/navigation_handle.h | 32 + .../content/public/browser/navigation_throttle.cc | 5 + .../content/public/browser/navigation_throttle.h | 9 + .../public/browser/navigator_connect_context.h | 36 - .../browser/navigator_connect_service_factory.h | 49 - .../public/browser/notification_event_dispatcher.h | 16 +- chromium/content/public/browser/page_navigator.cc | 2 + chromium/content/public/browser/page_navigator.h | 1 + .../content/public/browser/permission_manager.h | 12 +- chromium/content/public/browser/permission_type.h | 1 + .../public/browser/platform_notification_service.h | 13 +- chromium/content/public/browser/plugin_service.h | 36 - .../content/public/browser/plugin_service_filter.h | 7 - .../public/browser/presentation_service_delegate.h | 19 +- .../content/public/browser/presentation_session.h | 8 + .../public/browser/push_messaging_service.h | 29 +- chromium/content/public/browser/readback_types.h | 5 +- .../content/public/browser/render_frame_host.h | 26 +- .../content/public/browser/render_process_host.h | 18 +- chromium/content/public/browser/render_view_host.h | 9 - .../public/browser/render_widget_host_view.h | 24 +- .../browser/render_widget_host_view_mac_delegate.h | 3 + chromium/content/public/browser/resource_context.h | 2 - .../public/browser/resource_dispatcher_host.cc | 28 + .../public/browser/resource_dispatcher_host.h | 48 +- .../content/public/browser/resource_request_info.h | 5 + .../public/browser/screen_orientation_provider.cc | 2 +- .../public/browser/screen_orientation_provider.h | 3 +- .../public/browser/service_worker_context.h | 17 + .../public/browser/service_worker_usage_info.cc | 3 + .../public/browser/service_worker_usage_info.h | 1 + chromium/content/public/browser/site_instance.h | 24 +- .../browser/speech_recognition_session_config.cc | 3 + .../browser/speech_recognition_session_config.h | 1 + .../browser/speech_recognition_session_context.cc | 3 + .../browser/speech_recognition_session_context.h | 1 + .../content/public/browser/storage_partition.h | 2 - .../content/public/browser/tracing_controller.h | 62 +- .../content/public/browser/utility_process_host.h | 9 +- chromium/content/public/browser/web_contents.cc | 10 +- chromium/content/public/browser/web_contents.h | 42 +- .../public/browser/web_contents_delegate.cc | 9 +- .../content/public/browser/web_contents_delegate.h | 18 +- .../content/public/browser/web_contents_observer.h | 39 +- .../content/public/browser/web_ui_data_source.h | 3 - .../content/public/browser/zygote_handle_linux.h | 24 + .../content/public/browser/zygote_host_linux.h | 8 +- chromium/content/public/child/BUILD.gn | 2 + chromium/content/public/child/child_thread.h | 26 + chromium/content/public/child/request_peer.h | 21 - .../public/child/resource_dispatcher_delegate.h | 20 +- chromium/content/public/common/BUILD.gn | 43 +- chromium/content/public/common/appcache_info.h | 1 + .../content/public/common/background_sync.mojom | 19 +- chromium/content/public/common/bindings_policy.h | 10 + .../content/public/common/common_param_traits.cc | 72 +- .../content/public/common/common_param_traits.h | 45 +- .../public/common/common_param_traits_macros.h | 9 +- chromium/content/public/common/content_client.cc | 18 +- chromium/content/public/common/content_client.h | 28 +- .../content/public/common/content_constants.cc | 1 - .../content/public/common/content_descriptors.h | 10 +- chromium/content/public/common/content_features.cc | 76 +- chromium/content/public/common/content_features.h | 14 +- chromium/content/public/common/content_switches.cc | 233 +-- chromium/content/public/common/content_switches.h | 63 +- .../content/public/common/context_menu_params.cc | 2 + .../content/public/common/context_menu_params.h | 2 + chromium/content/public/common/drop_data.cc | 2 + chromium/content/public/common/drop_data.h | 1 + .../public/common/dwrite_font_platform_win.h | 47 - chromium/content/public/common/favicon_url.cc | 2 + chromium/content/public/common/favicon_url.h | 1 + .../common/feature_h264_with_openh264_ffmpeg.cc | 17 + .../common/feature_h264_with_openh264_ffmpeg.h | 22 + .../public/common/file_chooser_file_info.cc | 3 + .../content/public/common/file_chooser_file_info.h | 1 + .../content/public/common/file_chooser_params.cc | 2 + .../content/public/common/file_chooser_params.h | 1 + .../content/public/common/frame_navigate_params.cc | 3 + .../content/public/common/frame_navigate_params.h | 7 +- chromium/content/public/common/geoposition.cc | 2 + chromium/content/public/common/geoposition.h | 2 + chromium/content/public/common/gpu_memory_stats.cc | 25 - chromium/content/public/common/gpu_memory_stats.h | 50 - chromium/content/public/common/manifest.cc | 4 + chromium/content/public/common/manifest.h | 2 + chromium/content/public/common/media_metadata.cc | 23 + chromium/content/public/common/media_metadata.h | 40 + .../content/public/common/media_stream_request.cc | 8 + .../content/public/common/media_stream_request.h | 5 + .../content/public/common/mojo_channel_switches.cc | 32 +- .../content/public/common/mojo_channel_switches.h | 4 +- .../content/public/common/mojo_geoposition.mojom | 56 - .../content/public/common/mojo_shell_connection.h | 32 +- .../public/common/navigator_connect_client.cc | 21 - .../public/common/navigator_connect_client.h | 33 - .../public/common/notification_resources.cc | 16 + .../content/public/common/notification_resources.h | 36 + .../content/public/common/pepper_plugin_info.cc | 6 + .../content/public/common/pepper_plugin_info.h | 15 + .../content/public/common/permission_status.mojom | 12 - .../public/common/platform_notification_data.cc | 6 + .../public/common/platform_notification_data.h | 33 +- chromium/content/public/common/process_type.h | 4 +- .../content/public/common/push_event_payload.h | 37 + .../public/common/push_subscription_options.h | 32 + chromium/content/public/common/referrer.cc | 2 +- .../content/public/common/renderer_preferences.cc | 3 + .../content/public/common/renderer_preferences.h | 1 + .../content/public/common/resource_response.cc | 1 + .../public/common/resource_response_info.cc | 3 + .../content/public/common/resource_response_info.h | 9 + chromium/content/public/common/sandbox_init.h | 19 +- .../common/sandboxed_process_launcher_delegate.cc | 6 +- .../common/sandboxed_process_launcher_delegate.h | 7 +- chromium/content/public/common/service_registry.h | 12 + .../common/service_worker_event_status.mojom | 2 +- .../public/common/speech_recognition_result.cc | 3 + .../public/common/speech_recognition_result.h | 1 + chromium/content/public/common/ssl_status.cc | 2 + chromium/content/public/common/ssl_status.h | 1 + chromium/content/public/common/url_constants.cc | 8 +- chromium/content/public/common/url_constants.h | 7 +- chromium/content/public/common/url_utils.cc | 5 +- chromium/content/public/common/web_preferences.cc | 77 +- chromium/content/public/common/web_preferences.h | 21 +- chromium/content/public/common/webplugininfo.cc | 6 +- chromium/content/public/common/webplugininfo.h | 2 +- .../public/common/zygote_fork_delegate_linux.h | 1 + chromium/content/public/common/zygote_handle.h | 23 + chromium/content/public/gpu/BUILD.gn | 42 + chromium/content/public/gpu/DEPS | 10 + chromium/content/public/gpu/content_gpu_client.h | 27 + .../gpu/gpu_video_decode_accelerator_factory.cc | 72 + .../gpu/gpu_video_decode_accelerator_factory.h | 93 + chromium/content/public/plugin/BUILD.gn | 30 - .../content/public/plugin/content_plugin_client.h | 28 - chromium/content/public/renderer/BUILD.gn | 15 +- chromium/content/public/renderer/DEPS | 1 + .../public/renderer/content_renderer_client.cc | 16 +- .../public/renderer/content_renderer_client.h | 30 +- chromium/content/public/renderer/document_state.h | 2 +- .../content/public/renderer/media_stream_api.cc | 134 -- .../content/public/renderer/media_stream_api.h | 54 - .../public/renderer/media_stream_audio_renderer.h | 28 +- .../public/renderer/media_stream_audio_sink.cc | 11 +- .../content/public/renderer/media_stream_utils.cc | 139 ++ .../content/public/renderer/media_stream_utils.h | 64 + .../public/renderer/media_stream_video_sink.cc | 35 +- .../public/renderer/media_stream_video_sink.h | 47 +- .../public/renderer/render_frame_observer.h | 8 +- .../public/renderer/render_process_observer.h | 3 - chromium/content/public/renderer/render_thread.h | 25 +- chromium/content/public/renderer/render_view.h | 10 +- .../public/renderer/render_view_observer.cc | 35 +- .../content/public/renderer/render_view_observer.h | 2 +- .../content/public/renderer/resource_fetcher.h | 3 +- .../public/renderer/video_encode_accelerator.cc | 2 +- chromium/content/public/utility/BUILD.gn | 2 + .../public/utility/content_utility_client.h | 4 +- chromium/content/renderer/BUILD.gn | 69 +- chromium/content/renderer/DEPS | 4 +- chromium/content/renderer/OWNERS | 3 + .../accessibility/blink_ax_enum_conversion.cc | 7 +- .../renderer/accessibility/blink_ax_tree_source.cc | 71 +- .../accessibility/renderer_accessibility.cc | 32 +- .../renderer_accessibility_browsertest.cc | 100 - .../renderer/android/phone_number_detector.cc | 8 +- ...onous_compositor_external_begin_frame_source.cc | 10 +- ...ronous_compositor_external_begin_frame_source.h | 4 +- .../android/synchronous_compositor_factory.cc | 4 +- .../android/synchronous_compositor_factory.h | 8 +- .../android/synchronous_compositor_filter.cc | 74 +- .../android/synchronous_compositor_filter.h | 25 +- .../synchronous_compositor_output_surface.cc | 51 +- .../synchronous_compositor_output_surface.h | 17 +- .../android/synchronous_compositor_proxy.cc | 130 +- .../android/synchronous_compositor_proxy.h | 35 +- .../background_sync/background_sync_client_impl.cc | 57 +- .../background_sync/background_sync_client_impl.h | 22 +- chromium/content/renderer/battery_status/OWNERS | 1 - .../battery_status/battery_status_dispatcher.cc | 49 - .../battery_status/battery_status_dispatcher.h | 38 - .../battery_status_dispatcher_unittest.cc | 83 - .../renderer/bluetooth/bluetooth_dispatcher.cc | 194 +- .../renderer/bluetooth/bluetooth_dispatcher.h | 73 +- .../renderer/bluetooth/web_bluetooth_impl.cc | 69 +- .../renderer/bluetooth/web_bluetooth_impl.h | 53 +- .../renderer/browser_plugin/browser_plugin.cc | 99 +- .../renderer/browser_plugin/browser_plugin.h | 6 +- .../browser_plugin/browser_plugin_manager.cc | 26 +- .../browser_plugin/browser_plugin_manager.h | 3 - .../renderer/browser_render_view_browsertest.cc | 12 +- .../cache_storage/cache_storage_dispatcher.cc | 23 +- .../cache_storage/cache_storage_dispatcher.h | 14 +- .../webserviceworkercachestorage_impl.cc | 5 +- .../webserviceworkercachestorage_impl.h | 5 +- .../renderer/child_frame_compositing_helper.cc | 191 +- .../renderer/child_frame_compositing_helper.h | 32 +- chromium/content/renderer/device_sensors/OWNERS | 2 - .../content/renderer/devtools/devtools_agent.cc | 71 +- .../content/renderer/devtools/devtools_agent.h | 6 + .../render_widget_screen_metrics_emulator.cc | 177 ++ .../render_widget_screen_metrics_emulator.h | 89 + ...ender_widget_screen_metrics_emulator_delegate.h | 44 + .../renderer/devtools/v8_sampling_profiler.cc | 46 +- .../renderer/devtools/v8_sampling_profiler.h | 1 + .../devtools/v8_sampling_profiler_browsertest.cc | 1 + .../content/renderer/dom_serializer_browsertest.cc | 60 +- chromium/content/renderer/dom_storage/DEPS | 11 + .../dom_storage/dom_storage_cached_area.cc | 4 - .../renderer/dom_storage/dom_storage_cached_area.h | 2 - .../renderer/dom_storage/dom_storage_dispatcher.cc | 11 +- .../renderer/dom_storage/local_storage_area.cc | 57 + .../renderer/dom_storage/local_storage_area.h | 48 + .../dom_storage/local_storage_cached_area.cc | 336 ++++ .../dom_storage/local_storage_cached_area.h | 114 ++ .../dom_storage/local_storage_cached_areas.cc | 34 + .../dom_storage/local_storage_cached_areas.h | 50 + .../dom_storage/local_storage_namespace.cc | 40 + .../renderer/dom_storage/local_storage_namespace.h | 34 + .../renderer/dom_storage/webstoragearea_impl.cc | 4 - .../renderer/dom_storage/webstoragearea_impl.h | 1 - .../dom_storage/webstoragenamespace_impl.cc | 3 +- chromium/content/renderer/drop_data_builder.cc | 8 +- .../renderer/external_popup_menu_browsertest.cc | 8 +- .../multi_resolution_image_resource_fetcher.cc | 2 +- .../multi_resolution_image_resource_fetcher.h | 3 +- .../renderer/fetchers/resource_fetcher_impl.cc | 3 +- .../renderer/fetchers/resource_fetcher_impl.h | 3 +- chromium/content/renderer/frame_blame_context.cc | 41 + chromium/content/renderer/frame_blame_context.h | 26 + .../content/renderer/geolocation_dispatcher.cc | 21 +- chromium/content/renderer/geolocation_dispatcher.h | 13 +- .../content/renderer/gpu/compositor_dependencies.h | 2 + .../gpu/compositor_external_begin_frame_source.cc | 26 +- .../gpu/compositor_external_begin_frame_source.h | 6 +- .../renderer/gpu/compositor_output_surface.cc | 45 +- .../renderer/gpu/compositor_output_surface.h | 11 +- .../gpu/delegated_compositor_output_surface.cc | 6 + .../gpu/delegated_compositor_output_surface.h | 3 + .../renderer/gpu/gpu_benchmarking_extension.cc | 154 +- .../renderer/gpu/gpu_benchmarking_extension.h | 5 + .../content/renderer/gpu/mailbox_output_surface.cc | 3 + .../renderer/gpu/render_widget_compositor.cc | 216 ++- .../renderer/gpu/render_widget_compositor.h | 30 +- .../gpu/render_widget_compositor_delegate.h | 103 + .../gpu/render_widget_compositor_unittest.cc | 10 +- .../renderer/gpu/stream_texture_host_android.cc | 16 +- .../renderer/gpu/stream_texture_host_android.h | 13 +- chromium/content/renderer/history_controller.cc | 53 +- chromium/content/renderer/history_controller.h | 8 +- chromium/content/renderer/idle_user_detector.cc | 6 +- chromium/content/renderer/idle_user_detector.h | 4 +- .../image_downloader/image_downloader_impl.cc | 38 +- .../image_downloader/image_downloader_impl.h | 11 +- chromium/content/renderer/ime_event_guard.cc | 13 +- chromium/content/renderer/ime_event_guard.h | 1 - .../content/renderer/in_process_renderer_thread.h | 1 + .../content/renderer/input/input_event_filter.cc | 57 +- .../content/renderer/input/input_event_filter.h | 22 +- .../renderer/input/input_event_filter_unittest.cc | 281 ++- .../renderer/input/input_handler_manager.cc | 81 +- .../content/renderer/input/input_handler_manager.h | 51 +- .../renderer/input/input_handler_manager_client.h | 27 +- .../renderer/input/input_handler_wrapper.cc | 5 +- .../content/renderer/input/input_handler_wrapper.h | 3 +- .../renderer/input/main_thread_event_queue.cc | 122 ++ .../renderer/input/main_thread_event_queue.h | 121 ++ .../input/main_thread_event_queue_unittest.cc | 139 ++ .../renderer/input/render_widget_input_handler.cc | 157 +- .../renderer/input/render_widget_input_handler.h | 8 +- .../input/render_widget_input_handler_delegate.h | 13 + .../renderer/internal_document_state_data.cc | 5 +- .../renderer/internal_document_state_data.h | 12 +- .../gin_java_bridge_value_converter_unittest.cc | 2 + .../content/renderer/manifest/manifest_parser.cc | 7 +- .../content/renderer/manifest/manifest_parser.h | 6 +- .../renderer/manifest/manifest_parser_unittest.cc | 4 +- chromium/content/renderer/media/DEPS | 2 +- chromium/content/renderer/media/OWNERS | 7 +- .../renderer/media/aec_dump_message_filter.h | 3 +- .../renderer/media/android/media_info_loader.cc | 2 +- .../renderer/media/android/media_info_loader.h | 6 +- .../media/android/media_info_loader_unittest.cc | 4 +- .../media/android/media_source_delegate.cc | 85 +- .../renderer/media/android/media_source_delegate.h | 36 +- .../media/android/renderer_media_player_manager.cc | 31 +- .../media/android/renderer_media_player_manager.h | 6 +- .../android/renderer_media_session_manager.cc | 26 +- .../media/android/renderer_media_session_manager.h | 10 +- .../media/android/renderer_surface_view_manager.cc | 50 + .../media/android/renderer_surface_view_manager.h | 46 + .../media/android/stream_texture_factory.h | 9 +- .../media/android/stream_texture_factory_impl.cc | 17 +- .../media/android/stream_texture_factory_impl.h | 10 +- .../stream_texture_factory_synchronous_impl.cc | 60 +- .../stream_texture_factory_synchronous_impl.h | 11 +- .../media/android/webmediaplayer_android.cc | 560 ++---- .../media/android/webmediaplayer_android.h | 162 +- .../media/android/webmediasession_android.cc | 25 +- .../media/android/webmediasession_android.h | 4 +- .../android/webmediasession_android_unittest.cc | 35 +- chromium/content/renderer/media/audio_decoder.cc | 4 +- .../content/renderer/media/audio_device_factory.cc | 141 +- .../content/renderer/media/audio_device_factory.h | 81 +- .../renderer/media/audio_input_message_filter.cc | 6 +- .../renderer/media/audio_input_message_filter.h | 6 +- .../content/renderer/media/audio_message_filter.cc | 4 +- .../content/renderer/media/audio_message_filter.h | 6 +- .../media/audio_message_filter_unittest.cc | 6 +- .../renderer/media/audio_renderer_mixer_manager.cc | 68 +- .../renderer/media/audio_renderer_mixer_manager.h | 8 +- .../media/audio_renderer_mixer_manager_unittest.cc | 167 +- .../media/audio_repetition_detector_unittest.cc | 8 +- .../content/renderer/media/audio_track_recorder.cc | 373 ++-- .../content/renderer/media/audio_track_recorder.h | 19 +- .../media/audio_track_recorder_unittest.cc | 171 +- .../renderer/media/canvas_capture_handler.cc | 133 +- .../renderer/media/canvas_capture_handler.h | 19 +- .../media/canvas_capture_handler_unittest.cc | 80 +- .../renderer/media/cdm/pepper_cdm_wrapper.h | 6 +- .../renderer/media/cdm/pepper_cdm_wrapper_impl.cc | 16 +- .../renderer/media/cdm/pepper_cdm_wrapper_impl.h | 11 +- .../content/renderer/media/cdm/ppapi_decryptor.cc | 23 +- .../content/renderer/media/cdm/ppapi_decryptor.h | 23 +- .../content/renderer/media/cdm/proxy_media_keys.cc | 20 +- .../content/renderer/media/cdm/proxy_media_keys.h | 19 +- .../renderer/media/cdm/render_cdm_factory.cc | 6 +- .../renderer/media/cdm/render_cdm_factory.h | 2 +- .../media/html_video_element_capturer_source.cc | 15 +- .../media/html_video_element_capturer_source.h | 11 +- .../html_video_element_capturer_source_unittest.cc | 14 +- .../renderer/media/media_interface_provider.cc | 68 + .../renderer/media/media_interface_provider.h | 49 + .../renderer/media/media_permission_dispatcher.cc | 109 +- .../renderer/media/media_permission_dispatcher.h | 55 +- .../media/media_permission_dispatcher_impl.cc | 99 - .../media/media_permission_dispatcher_impl.h | 69 - .../media/media_permission_dispatcher_proxy.cc | 161 -- .../media/media_permission_dispatcher_proxy.h | 57 - .../renderer/media/media_recorder_handler.cc | 58 +- .../renderer/media/media_recorder_handler.h | 16 +- .../media/media_recorder_handler_unittest.cc | 74 +- chromium/content/renderer/media/media_stream.cc | 2 +- .../media/media_stream_audio_level_calculator.cc | 35 +- .../media/media_stream_audio_level_calculator.h | 49 +- .../renderer/media/media_stream_audio_processor.cc | 79 +- .../renderer/media/media_stream_audio_processor.h | 16 +- .../media/media_stream_audio_processor_options.cc | 235 ++- .../media/media_stream_audio_processor_options.h | 48 +- .../media/media_stream_audio_processor_unittest.cc | 197 +- .../renderer/media/media_stream_audio_source.cc | 37 +- .../renderer/media/media_stream_audio_source.h | 60 +- .../renderer/media/media_stream_audio_track.cc | 33 +- .../renderer/media/media_stream_audio_track.h | 30 +- .../content/renderer/media/media_stream_center.cc | 17 +- .../media/media_stream_constraints_util.cc | 187 +- .../renderer/media/media_stream_constraints_util.h | 77 +- .../media_stream_constraints_util_unittest.cc | 137 +- .../renderer/media/media_stream_dispatcher.h | 2 +- .../media/media_stream_dispatcher_unittest.cc | 34 +- .../media/media_stream_renderer_factory_impl.cc | 104 +- .../content/renderer/media/media_stream_track.cc | 4 +- .../media/media_stream_video_capturer_source.cc | 50 +- .../media/media_stream_video_capturer_source.h | 10 +- .../media_stream_video_capturer_source_unittest.cc | 100 +- .../media/media_stream_video_renderer_sink.cc | 10 +- .../media/media_stream_video_renderer_sink.h | 8 +- .../media_stream_video_renderer_sink_unittest.cc | 45 +- .../renderer/media/media_stream_video_source.cc | 328 ++-- .../renderer/media/media_stream_video_source.h | 6 + .../media/media_stream_video_source_unittest.cc | 276 ++- .../renderer/media/media_stream_video_track.cc | 19 +- .../renderer/media/media_stream_video_track.h | 4 +- .../media/media_stream_video_track_unittest.cc | 69 +- chromium/content/renderer/media/midi_dispatcher.cc | 31 +- chromium/content/renderer/media/midi_dispatcher.h | 16 +- .../content/renderer/media/midi_message_filter.cc | 1 - .../content/renderer/media/midi_message_filter.h | 12 +- .../renderer/media/mock_constraint_factory.cc | 50 + .../renderer/media/mock_constraint_factory.h | 37 + .../renderer/media/mock_data_channel_impl.h | 2 +- .../media/mock_media_constraint_factory.cc | 109 -- .../renderer/media/mock_media_constraint_factory.h | 38 - .../renderer/media/mock_media_stream_registry.cc | 15 +- .../renderer/media/mock_media_stream_video_sink.h | 13 + .../media/mock_media_stream_video_source.cc | 20 +- .../media/mock_media_stream_video_source.h | 6 + .../renderer/media/mock_peer_connection_impl.cc | 9 +- .../renderer/media/mock_peer_connection_impl.h | 24 +- .../media/peer_connection_identity_store.cc | 9 +- .../media/peer_connection_identity_store.h | 2 +- .../renderer/media/peer_connection_tracker.cc | 348 ++-- .../renderer/media/peer_connection_tracker.h | 49 +- .../media/peer_connection_tracker_unittest.cc | 82 + .../pepper_to_video_track_adapter_unittest.cc | 2 +- .../renderer/media/remote_media_stream_impl.cc | 27 +- .../renderer/media/remote_media_stream_impl.h | 13 +- .../content/renderer/media/render_media_client.cc | 4 +- .../content/renderer/media/render_media_client.h | 10 +- .../renderer/media/render_media_client_unittest.cc | 2 +- .../content/renderer/media/render_media_log.cc | 125 +- chromium/content/renderer/media/render_media_log.h | 39 +- .../renderer/media/render_media_log_unittest.cc | 2 +- .../renderer_gpu_video_accelerator_factories.cc | 97 +- .../renderer_gpu_video_accelerator_factories.h | 28 +- .../renderer/media/renderer_webaudiodevice_impl.cc | 35 +- .../renderer/media/renderer_webaudiodevice_impl.h | 14 +- .../media/renderer_webmediaplayer_delegate.cc | 160 +- .../media/renderer_webmediaplayer_delegate.h | 74 +- ...renderer_webmediaplayer_delegate_browsertest.cc | 266 +++ .../renderer/media/renderer_webmidiaccessor_impl.h | 4 +- chromium/content/renderer/media/rtc_certificate.cc | 5 +- chromium/content/renderer/media/rtc_certificate.h | 2 +- .../renderer/media/rtc_certificate_generator.cc | 34 +- .../renderer/media/rtc_certificate_generator.h | 2 +- .../renderer/media/rtc_data_channel_handler.cc | 13 +- .../renderer/media/rtc_data_channel_handler.h | 9 +- .../media/rtc_data_channel_handler_unittest.cc | 8 +- .../renderer/media/rtc_dtmf_sender_handler.h | 2 +- .../renderer/media/rtc_media_constraints.cc | 107 - .../content/renderer/media/rtc_media_constraints.h | 51 - .../renderer/media/rtc_peer_connection_handler.cc | 300 ++- .../renderer/media/rtc_peer_connection_handler.h | 21 +- .../media/rtc_peer_connection_handler_unittest.cc | 125 +- .../content/renderer/media/rtc_video_decoder.cc | 118 +- .../content/renderer/media/rtc_video_decoder.h | 41 +- .../renderer/media/rtc_video_decoder_factory.cc | 3 +- .../renderer/media/rtc_video_decoder_factory.h | 2 +- .../renderer/media/rtc_video_decoder_unittest.cc | 113 +- .../content/renderer/media/rtc_video_encoder.cc | 18 +- .../content/renderer/media/rtc_video_encoder.h | 4 +- .../renderer/media/rtc_video_encoder_factory.cc | 2 +- .../renderer/media/rtc_video_encoder_factory.h | 2 +- .../media/speech_recognition_audio_sink.cc | 7 +- .../renderer/media/speech_recognition_audio_sink.h | 13 +- .../speech_recognition_audio_sink_unittest.cc | 39 +- .../content/renderer/media/track_audio_renderer.cc | 390 ++++ .../content/renderer/media/track_audio_renderer.h | 182 ++ .../renderer/media/user_media_client_impl.cc | 203 +- .../renderer/media/user_media_client_impl.h | 22 +- .../media/user_media_client_impl_unittest.cc | 95 +- .../content/renderer/media/video_capture_impl.cc | 21 +- .../content/renderer/media/video_capture_impl.h | 7 +- .../renderer/media/video_capture_impl_manager.cc | 13 + .../renderer/media/video_capture_impl_manager.h | 6 +- .../media/video_capture_impl_manager_unittest.cc | 4 +- .../renderer/media/video_capture_impl_unittest.cc | 4 +- .../media/video_capture_message_filter_unittest.cc | 17 +- .../content/renderer/media/video_track_adapter.cc | 8 +- .../content/renderer/media/video_track_recorder.cc | 99 +- .../content/renderer/media/video_track_recorder.h | 7 +- .../media/video_track_recorder_unittest.cc | 43 +- .../media/video_track_to_pepper_adapter.cc | 12 +- .../renderer/media/video_track_to_pepper_adapter.h | 4 +- .../video_track_to_pepper_adapter_unittest.cc | 4 +- .../renderer/media/webaudio_capturer_source.cc | 87 +- .../renderer/media/webaudio_capturer_source.h | 52 +- .../content/renderer/media/webmediaplayer_ms.cc | 177 +- .../content/renderer/media/webmediaplayer_ms.h | 80 +- .../renderer/media/webmediaplayer_ms_compositor.cc | 79 +- .../renderer/media/webmediaplayer_ms_compositor.h | 4 +- .../renderer/media/webmediaplayer_ms_unittest.cc | 146 +- .../webrtc/media_stream_remote_audio_track.cc | 18 +- .../media/webrtc/media_stream_remote_audio_track.h | 9 +- .../webrtc/media_stream_remote_video_source.cc | 35 +- .../webrtc/media_stream_remote_video_source.h | 8 +- .../media_stream_remote_video_source_unittest.cc | 29 +- .../media/webrtc/media_stream_track_metrics.cc | 2 +- .../media/webrtc/media_stream_track_metrics.h | 2 +- .../webrtc/media_stream_track_metrics_unittest.cc | 15 +- .../media/webrtc/media_stream_video_webrtc_sink.cc | 145 +- .../media/webrtc/media_stream_video_webrtc_sink.h | 14 +- .../mock_peer_connection_dependency_factory.cc | 224 +-- .../mock_peer_connection_dependency_factory.h | 132 +- .../webrtc/peer_connection_dependency_factory.cc | 285 +-- .../webrtc/peer_connection_dependency_factory.h | 46 +- .../peer_connection_dependency_factory_unittest.cc | 4 +- .../renderer/media/webrtc/stun_field_trial.cc | 2 +- .../renderer/media/webrtc/stun_field_trial.h | 2 +- .../content/renderer/media/webrtc/track_observer.h | 2 +- .../media/webrtc/webrtc_audio_sink_adapter.cc | 2 +- .../media/webrtc/webrtc_audio_sink_adapter.h | 5 +- .../webrtc/webrtc_local_audio_track_adapter.cc | 87 +- .../webrtc/webrtc_local_audio_track_adapter.h | 45 +- .../webrtc_local_audio_track_adapter_unittest.cc | 28 +- .../media/webrtc/webrtc_media_stream_adapter.cc | 19 +- .../media/webrtc/webrtc_media_stream_adapter.h | 4 +- .../webrtc/webrtc_media_stream_adapter_unittest.cc | 25 +- .../media/webrtc/webrtc_video_capturer_adapter.cc | 20 +- .../media/webrtc/webrtc_video_capturer_adapter.h | 6 +- .../renderer/media/webrtc_audio_capturer.cc | 173 +- .../content/renderer/media/webrtc_audio_capturer.h | 32 +- .../media/webrtc_audio_capturer_unittest.cc | 57 +- .../renderer/media/webrtc_audio_device_impl.cc | 124 +- .../renderer/media/webrtc_audio_device_impl.h | 20 +- .../renderer/media/webrtc_audio_device_not_impl.cc | 3 +- .../renderer/media/webrtc_audio_device_not_impl.h | 2 +- .../renderer/media/webrtc_audio_renderer.cc | 188 +- .../content/renderer/media/webrtc_audio_renderer.h | 29 +- .../media/webrtc_audio_renderer_unittest.cc | 189 +- .../renderer/media/webrtc_identity_service.cc | 3 + .../renderer/media/webrtc_identity_service.h | 1 + .../media/webrtc_identity_service_unittest.cc | 2 +- .../renderer/media/webrtc_local_audio_renderer.cc | 356 ---- .../renderer/media/webrtc_local_audio_renderer.h | 183 -- .../media/webrtc_local_audio_source_provider.h | 8 +- .../webrtc_local_audio_source_provider_unittest.cc | 23 +- .../renderer/media/webrtc_local_audio_track.cc | 116 +- .../renderer/media/webrtc_local_audio_track.h | 64 +- .../media/webrtc_local_audio_track_unittest.cc | 186 +- .../content/renderer/media/webrtc_uma_histograms.h | 1 + .../renderer/memory_benchmarking_extension.cc | 88 - .../renderer/memory_benchmarking_extension.h | 45 - .../renderer/mojo/blink_service_registry_impl.cc | 28 + .../renderer/mojo/blink_service_registry_impl.h | 37 + .../renderer/mojo/service_registry_js_wrapper.cc | 75 +- .../renderer/mojo/service_registry_js_wrapper.h | 28 +- .../content/renderer/mojo_bindings_controller.cc | 21 +- .../content/renderer/mojo_bindings_controller.h | 8 +- chromium/content/renderer/mojo_context_state.cc | 100 +- chromium/content/renderer/mojo_context_state.h | 5 +- chromium/content/renderer/mus/BUILD.gn | 14 +- .../renderer/mus/compositor_mus_connection.cc | 36 +- .../renderer/mus/compositor_mus_connection.h | 21 +- .../mus/compositor_mus_connection_unittest.cc | 454 +++++ .../renderer/mus/render_widget_mus_connection.cc | 27 +- .../renderer/mus/render_widget_mus_connection.h | 14 +- .../render_widget_window_tree_client_factory.cc | 13 +- .../renderer/notification_permission_dispatcher.cc | 14 +- .../renderer/notification_permission_dispatcher.h | 6 +- .../content/renderer/npapi/plugin_channel_host.cc | 168 -- .../content/renderer/npapi/plugin_channel_host.h | 77 - .../renderer/npapi/webplugin_delegate_proxy.cc | 1088 ----------- .../renderer/npapi/webplugin_delegate_proxy.h | 286 --- chromium/content/renderer/npapi/webplugin_impl.cc | 1228 ------------ chromium/content/renderer/npapi/webplugin_impl.h | 327 ---- .../renderer/npapi/webplugin_impl_unittest.cc | 235 --- .../web_trial_token_validator_impl.cc | 22 + .../origin_trials/web_trial_token_validator_impl.h | 37 + .../renderer/p2p/filtering_network_manager.cc | 4 +- .../renderer/p2p/filtering_network_manager.h | 4 +- .../p2p/filtering_network_manager_unittest.cc | 12 +- .../content/renderer/p2p/host_address_request.h | 2 +- .../content/renderer/p2p/ipc_network_manager.cc | 17 +- .../content/renderer/p2p/ipc_network_manager.h | 8 +- .../renderer/p2p/ipc_network_manager_unittest.cc | 55 +- .../content/renderer/p2p/ipc_socket_factory.cc | 5 +- .../content/renderer/p2p/network_list_observer.h | 7 +- chromium/content/renderer/p2p/port_allocator.cc | 4 +- chromium/content/renderer/p2p/port_allocator.h | 6 +- chromium/content/renderer/p2p/socket_dispatcher.cc | 4 +- chromium/content/renderer/p2p/socket_dispatcher.h | 10 +- chromium/content/renderer/pepper/DEPS | 2 +- .../renderer/pepper/content_decryptor_delegate.cc | 5 +- .../pepper/content_renderer_pepper_host_factory.cc | 58 +- .../pepper/content_renderer_pepper_host_factory.h | 2 +- .../content/renderer/pepper/event_conversion.cc | 5 +- .../content/renderer/pepper/fullscreen_container.h | 2 +- chromium/content/renderer/pepper/host_globals.cc | 6 +- .../renderer/pepper/host_var_tracker_unittest.cc | 1 - .../content/renderer/pepper/message_channel.cc | 2 - chromium/content/renderer/pepper/pepper_broker.cc | 3 + chromium/content/renderer/pepper/pepper_broker.h | 1 + .../renderer/pepper/pepper_compositor_host.cc | 12 +- .../renderer/pepper/pepper_compositor_host.h | 5 +- .../renderer/pepper/pepper_file_chooser_host.cc | 1 - .../pepper/pepper_file_chooser_host_unittest.cc | 2 +- .../renderer/pepper/pepper_graphics_2d_host.cc | 34 +- .../pepper/pepper_media_stream_video_track_host.cc | 44 +- .../pepper/pepper_media_stream_video_track_host.h | 7 +- .../pepper/pepper_platform_video_capture.cc | 2 + .../renderer/pepper/pepper_plugin_instance_impl.cc | 160 +- .../renderer/pepper/pepper_plugin_instance_impl.h | 10 +- .../pepper/pepper_proxy_channel_delegate_impl.cc | 9 - .../renderer/pepper/pepper_url_loader_host.cc | 2 +- .../renderer/pepper/pepper_url_request_unittest.cc | 30 +- .../renderer/pepper/pepper_video_capture_host.cc | 3 + .../renderer/pepper/pepper_video_capture_host.h | 1 + .../renderer/pepper/pepper_video_decoder_host.cc | 28 +- .../renderer/pepper/pepper_video_decoder_host.h | 1 + .../renderer/pepper/pepper_video_encoder_host.cc | 34 +- .../renderer/pepper/pepper_video_encoder_host.h | 11 +- .../renderer/pepper/pepper_video_source_host.cc | 14 +- .../renderer/pepper/pepper_webplugin_impl.cc | 100 +- .../renderer/pepper/pepper_webplugin_impl.h | 6 +- .../renderer/pepper/pepper_websocket_host.cc | 24 +- .../renderer/pepper/pepper_websocket_host.h | 10 +- .../pepper/plugin_instance_throttler_impl.cc | 2 +- chromium/content/renderer/pepper/plugin_module.cc | 7 - chromium/content/renderer/pepper/plugin_object.cc | 22 +- .../renderer/pepper/plugin_power_saver_helper.cc | 3 + .../renderer/pepper/plugin_power_saver_helper.h | 1 + .../renderer/pepper/ppb_graphics_3d_impl.cc | 95 +- .../content/renderer/pepper/ppb_graphics_3d_impl.h | 35 +- .../content/renderer/pepper/ppb_image_data_impl.cc | 12 +- .../content/renderer/pepper/ppb_image_data_impl.h | 4 +- .../renderer/pepper/ppb_video_decoder_impl.cc | 28 +- .../renderer/pepper/ppb_video_decoder_impl.h | 1 + .../content/renderer/pepper/resource_converter.cc | 5 +- .../renderer/pepper/resource_creation_impl.cc | 4 +- .../renderer/pepper/resource_creation_impl.h | 2 +- .../renderer/pepper/url_response_info_util.cc | 4 +- .../content/renderer/pepper/v8_var_converter.h | 1 + .../renderer/pepper/v8_var_converter_unittest.cc | 2 + .../content/renderer/pepper/video_decoder_shim.cc | 25 +- .../content/renderer/pepper/video_encoder_shim.cc | 22 +- .../presentation/presentation_connection_client.cc | 5 +- .../presentation/presentation_connection_client.h | 2 +- .../presentation/presentation_dispatcher.cc | 113 +- .../presentation/presentation_dispatcher.h | 35 +- .../push_messaging/push_messaging_dispatcher.cc | 70 +- .../push_messaging/push_messaging_dispatcher.h | 8 +- chromium/content/renderer/raster_worker_pool.cc | 201 +- chromium/content/renderer/raster_worker_pool.h | 44 +- .../renderer/raster_worker_pool_unittest.cc | 6 +- chromium/content/renderer/render_frame_impl.cc | 1775 +++++++++-------- chromium/content/renderer/render_frame_impl.h | 236 ++- .../renderer/render_frame_impl_browsertest.cc | 92 +- chromium/content/renderer/render_frame_proxy.cc | 145 +- chromium/content/renderer/render_frame_proxy.h | 24 +- chromium/content/renderer/render_process_impl.cc | 42 +- chromium/content/renderer/render_thread_impl.cc | 427 ++-- chromium/content/renderer/render_thread_impl.h | 72 +- .../renderer/render_thread_impl_browsertest.cc | 32 +- .../content/renderer/render_view_browsertest.cc | 929 +++------ .../renderer/render_view_browsertest_mac.mm | 16 +- chromium/content/renderer/render_view_impl.cc | 875 +++------ chromium/content/renderer/render_view_impl.h | 141 +- .../renderer/render_view_mouse_lock_dispatcher.cc | 4 +- chromium/content/renderer/render_view_win.cc | 5 + chromium/content/renderer/render_widget.cc | 1095 ++++++----- chromium/content/renderer/render_widget.h | 174 +- .../content/renderer/render_widget_browsertest.cc | 25 +- .../renderer/render_widget_fullscreen_pepper.cc | 14 +- .../renderer/render_widget_fullscreen_pepper.h | 6 +- .../renderer/render_widget_owner_delegate.h | 58 + .../content/renderer/render_widget_unittest.cc | 110 +- chromium/content/renderer/renderer.sb | 4 + .../renderer/renderer_blink_platform_impl.cc | 294 +-- .../renderer/renderer_blink_platform_impl.h | 66 +- chromium/content/renderer/renderer_main.cc | 43 +- .../renderer_main_platform_delegate_linux.cc | 2 +- .../renderer_main_platform_delegate_win.cc | 17 +- .../renderer_webapplicationcachehost_impl.cc | 3 +- .../content/renderer/renderer_webcookiejar_impl.cc | 5 - .../content/renderer/renderer_webcookiejar_impl.h | 3 - .../content/renderer/resizing_mode_selector.cc | 5 +- chromium/content/renderer/resizing_mode_selector.h | 6 +- chromium/content/renderer/savable_resources.cc | 12 +- .../scheduler/resource_dispatch_throttler.cc | 26 +- .../scheduler/resource_dispatch_throttler.h | 3 +- .../resource_dispatch_throttler_unittest.cc | 40 + .../embedded_worker_devtools_agent.cc | 1 - .../service_worker/embedded_worker_dispatcher.cc | 23 +- .../service_worker/embedded_worker_dispatcher.h | 1 + .../service_worker_context_client.cc | 226 ++- .../service_worker/service_worker_context_client.h | 54 +- ..._shared_worker_content_settings_client_proxy.cc | 12 - ...d_shared_worker_content_settings_client_proxy.h | 3 - .../shared_worker/embedded_shared_worker_stub.cc | 23 +- .../shared_worker/embedded_shared_worker_stub.h | 5 +- .../content/renderer/shared_worker_repository.cc | 2 + .../content/renderer/shared_worker_repository.h | 2 + .../renderer/skia_benchmarking_extension.cc | 7 +- .../content/renderer/text_input_client_observer.cc | 3 +- .../content/renderer/top_level_blame_context.cc | 25 + .../content/renderer/top_level_blame_context.h | 25 + chromium/content/renderer/usb/DEPS | 3 +- chromium/content/renderer/usb/type_converters.cc | 35 +- chromium/content/renderer/usb/type_converters.h | 4 +- .../content/renderer/usb/web_usb_client_impl.cc | 56 +- .../content/renderer/usb/web_usb_client_impl.h | 11 +- .../content/renderer/usb/web_usb_device_impl.cc | 250 ++- .../content/renderer/usb/web_usb_device_impl.h | 20 +- chromium/content/renderer/vr/vr_dispatcher.cc | 9 +- chromium/content/renderer/vr/vr_dispatcher.h | 8 +- chromium/content/renderer/vr/vr_type_converters.cc | 16 +- chromium/content/renderer/vr/vr_type_converters.h | 36 +- .../renderer/wake_lock/wake_lock_dispatcher.h | 2 +- .../renderer/webgraphicscontext3d_provider_impl.cc | 10 + .../renderer/webgraphicscontext3d_provider_impl.h | 8 + chromium/content/renderer/websharedworker_proxy.cc | 4 +- chromium/content/renderer/websharedworker_proxy.h | 11 +- chromium/content/shell/common/DEPS | 1 - ...layout_test_bluetooth_fake_adapter_setter.mojom | 14 + .../layout_test/layout_test_content_client.cc | 24 + .../layout_test/layout_test_content_client.h | 24 + .../common/layout_test/layout_test_messages.h | 19 +- .../common/layout_test/layout_test_switches.cc | 48 + .../common/layout_test/layout_test_switches.h | 28 + .../content/shell/common/shell_content_client.cc | 31 +- .../content/shell/common/shell_content_client.h | 5 + chromium/content/shell/common/shell_messages.h | 47 +- chromium/content/shell/common/shell_switches.cc | 46 +- chromium/content/shell/common/shell_switches.h | 17 +- chromium/content/shell/shell_resources.grd | 18 + chromium/content/utility/BUILD.gn | 23 +- .../content/utility/in_process_utility_thread.h | 1 + .../content/utility/utility_blink_platform_impl.h | 1 + .../utility/utility_process_control_impl.cc | 19 +- .../content/utility/utility_process_control_impl.h | 2 +- chromium/content/utility/utility_thread_impl.cc | 31 +- chromium/content/utility/utility_thread_impl.h | 12 +- .../utility/webthread_impl_for_utility_thread.cc | 4 +- .../utility/webthread_impl_for_utility_thread.h | 4 +- chromium/content/zygote/zygote_linux.cc | 15 +- chromium/content/zygote/zygote_main_linux.cc | 37 +- 2642 files changed, 105078 insertions(+), 110610 deletions(-) delete mode 100644 chromium/content/README create mode 100644 chromium/content/README.md create mode 100644 chromium/content/app/android/download_main.cc delete mode 100644 chromium/content/browser/accessibility/android_hit_testing_browsertest.cc create mode 100644 chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm create mode 100644 chromium/content/browser/accessibility/hit_testing_browsertest.cc create mode 100644 chromium/content/browser/android/content_view_core_impl_observer.h create mode 100644 chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.cc create mode 100644 chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.h create mode 100644 chromium/content/browser/android/service_registry_android_impl.cc create mode 100644 chromium/content/browser/android/service_registry_android_impl.h delete mode 100644 chromium/content/browser/background_sync/background_sync_power_observer.cc delete mode 100644 chromium/content/browser/background_sync/background_sync_power_observer.h delete mode 100644 chromium/content/browser/background_sync/background_sync_power_observer_unittest.cc delete mode 100644 chromium/content/browser/background_sync/background_sync_registration_handle.cc delete mode 100644 chromium/content/browser/background_sync/background_sync_registration_handle.h create mode 100644 chromium/content/browser/blob_storage/blob_async_transport_request_builder_unittest.cc delete mode 100644 chromium/content/browser/blob_storage/blob_async_transport_strategy_unittest.cc create mode 100644 chromium/content/browser/blob_storage/blob_dispatcher_host.cc create mode 100644 chromium/content/browser/blob_storage/blob_dispatcher_host.h create mode 100644 chromium/content/browser/blob_storage/blob_dispatcher_host_unittest.cc create mode 100644 chromium/content/browser/bluetooth/README.md create mode 100644 chromium/content/browser/bluetooth/bluetooth_blacklist.cc create mode 100644 chromium/content/browser/bluetooth/bluetooth_blacklist.h create mode 100644 chromium/content/browser/bluetooth/bluetooth_blacklist_unittest.cc create mode 100644 chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc create mode 100644 chromium/content/browser/bluetooth/web_bluetooth_service_impl.h delete mode 100644 chromium/content/browser/compositor/browser_compositor_view_mac.h delete mode 100644 chromium/content/browser/compositor/browser_compositor_view_mac.mm delete mode 100644 chromium/content/browser/compositor/delegated_frame_host.cc delete mode 100644 chromium/content/browser/compositor/delegated_frame_host.h create mode 100644 chromium/content/browser/compositor/gl_helper.cc create mode 100644 chromium/content/browser/compositor/gl_helper.h create mode 100644 chromium/content/browser/compositor/gl_helper_benchmark.cc create mode 100644 chromium/content/browser/compositor/gl_helper_readback_support.cc create mode 100644 chromium/content/browser/compositor/gl_helper_readback_support.h create mode 100644 chromium/content/browser/compositor/gl_helper_scaling.cc create mode 100644 chromium/content/browser/compositor/gl_helper_scaling.h create mode 100644 chromium/content/browser/compositor/gl_helper_unittest.cc delete mode 100644 chromium/content/browser/compositor/resize_lock.cc delete mode 100644 chromium/content/browser/compositor/resize_lock.h delete mode 100644 chromium/content/browser/device_monitor_mac.h delete mode 100644 chromium/content/browser/device_monitor_mac.mm delete mode 100644 chromium/content/browser/device_monitor_udev.cc delete mode 100644 chromium/content/browser/device_monitor_udev.h create mode 100644 chromium/content/browser/device_sensors/device_sensor_message_filter.cc create mode 100644 chromium/content/browser/device_sensors/device_sensor_message_filter.h create mode 100644 chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc create mode 100644 chromium/content/browser/download/docs/save-page-as.md create mode 100644 chromium/content/browser/download/download_destination_observer.h delete mode 100644 chromium/content/browser/fileapi/blob_storage_host.cc delete mode 100644 chromium/content/browser/fileapi/blob_storage_host.h create mode 100644 chromium/content/browser/frame_host/PRESUBMIT.py create mode 100644 chromium/content/browser/frame_host/navigation_handle_impl_browsertest.cc create mode 100644 chromium/content/browser/frame_host/traced_frame_tree_node.cc create mode 100644 chromium/content/browser/frame_host/traced_frame_tree_node.h delete mode 100644 chromium/content/browser/gpu/gpu_arc_video_service_host.cc delete mode 100644 chromium/content/browser/gpu/gpu_arc_video_service_host.h create mode 100644 chromium/content/browser/indexed_db/leveldb_coding_scheme.md create mode 100644 chromium/content/browser/leveldb_wrapper_impl.cc create mode 100644 chromium/content/browser/leveldb_wrapper_impl.h create mode 100644 chromium/content/browser/media/android/browser_surface_view_manager.cc create mode 100644 chromium/content/browser/media/android/browser_surface_view_manager.h delete mode 100644 chromium/content/browser/media/android/media_session.cc delete mode 100644 chromium/content/browser/media/android/media_session.h delete mode 100644 chromium/content/browser/media/android/media_session_browsertest.cc delete mode 100644 chromium/content/browser/media/android/media_session_observer.h delete mode 100644 chromium/content/browser/media/android/media_session_uma_helper.cc delete mode 100644 chromium/content/browser/media/android/media_session_uma_helper.h delete mode 100644 chromium/content/browser/media/android/media_session_uma_helper_unittest.cc create mode 100644 chromium/content/browser/media/capture/cursor_renderer_mac.h create mode 100644 chromium/content/browser/media/capture/cursor_renderer_mac.mm create mode 100644 chromium/content/browser/media/capture/window_activity_tracker.cc create mode 100644 chromium/content/browser/media/capture/window_activity_tracker_mac.h create mode 100644 chromium/content/browser/media/capture/window_activity_tracker_mac.mm create mode 100644 chromium/content/browser/media/session/OWNERS create mode 100644 chromium/content/browser/media/session/media_session.cc create mode 100644 chromium/content/browser/media/session/media_session.h create mode 100644 chromium/content/browser/media/session/media_session_browsertest.cc create mode 100644 chromium/content/browser/media/session/media_session_controller.cc create mode 100644 chromium/content/browser/media/session/media_session_controller.h create mode 100644 chromium/content/browser/media/session/media_session_controller_unittest.cc create mode 100644 chromium/content/browser/media/session/media_session_controllers_manager.cc create mode 100644 chromium/content/browser/media/session/media_session_controllers_manager.h create mode 100644 chromium/content/browser/media/session/media_session_delegate.h create mode 100644 chromium/content/browser/media/session/media_session_delegate_android.cc create mode 100644 chromium/content/browser/media/session/media_session_delegate_android.h create mode 100644 chromium/content/browser/media/session/media_session_delegate_android_browsertest.cc create mode 100644 chromium/content/browser/media/session/media_session_delegate_default.cc create mode 100644 chromium/content/browser/media/session/media_session_delegate_default_browsertest.cc create mode 100644 chromium/content/browser/media/session/media_session_observer.h create mode 100644 chromium/content/browser/media/session/media_session_uma_helper.cc create mode 100644 chromium/content/browser/media/session/media_session_uma_helper.h create mode 100644 chromium/content/browser/media/session/media_session_uma_helper_unittest.cc create mode 100644 chromium/content/browser/media/session/media_session_visibility_browsertest.cc create mode 100644 chromium/content/browser/media/session/mock_media_session_observer.cc create mode 100644 chromium/content/browser/media/session/mock_media_session_observer.h create mode 100644 chromium/content/browser/media/webrtc/OWNERS create mode 100644 chromium/content/browser/media/webrtc/webrtc_audio_debug_recordings_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_getusermedia_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_identity_store.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_identity_store.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_identity_store_backend.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_identity_store_backend.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_identity_store_unittest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_message_handler.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_message_handler.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_ui.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_ui.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_ui_observer.h create mode 100644 chromium/content/browser/media/webrtc/webrtc_internals_unittest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_ip_permissions_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc create mode 100644 chromium/content/browser/media/webrtc/webrtc_webcam_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_audio_debug_recordings_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_getusermedia_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_identity_store.cc delete mode 100644 chromium/content/browser/media/webrtc_identity_store.h delete mode 100644 chromium/content/browser/media/webrtc_identity_store_backend.cc delete mode 100644 chromium/content/browser/media/webrtc_identity_store_backend.h delete mode 100644 chromium/content/browser/media/webrtc_identity_store_unittest.cc delete mode 100644 chromium/content/browser/media/webrtc_internals.cc delete mode 100644 chromium/content/browser/media/webrtc_internals.h delete mode 100644 chromium/content/browser/media/webrtc_internals_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_internals_message_handler.cc delete mode 100644 chromium/content/browser/media/webrtc_internals_message_handler.h delete mode 100644 chromium/content/browser/media/webrtc_internals_ui.cc delete mode 100644 chromium/content/browser/media/webrtc_internals_ui.h delete mode 100644 chromium/content/browser/media/webrtc_internals_ui_observer.h delete mode 100644 chromium/content/browser/media/webrtc_internals_unittest.cc delete mode 100644 chromium/content/browser/media/webrtc_ip_permissions_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_media_recorder_browsertest.cc delete mode 100644 chromium/content/browser/media/webrtc_webcam_browsertest.cc delete mode 100644 chromium/content/browser/memory/memory_pressure_controller.cc delete mode 100644 chromium/content/browser/memory/memory_pressure_controller.h delete mode 100644 chromium/content/browser/memory/memory_pressure_controller_browsertest.cc create mode 100644 chromium/content/browser/memory/memory_pressure_controller_impl.cc create mode 100644 chromium/content/browser/memory/memory_pressure_controller_impl.h create mode 100644 chromium/content/browser/memory/memory_pressure_controller_impl_browsertest.cc delete mode 100644 chromium/content/browser/mojo/OWNERS create mode 100644 chromium/content/browser/mojo/constants.cc create mode 100644 chromium/content/browser/mojo/constants.h create mode 100644 chromium/content/browser/mojo/mojo_child_connection.cc create mode 100644 chromium/content/browser/mojo/mojo_child_connection.h delete mode 100644 chromium/content/browser/mojo/mojo_shell_client_host.cc delete mode 100644 chromium/content/browser/mojo/mojo_shell_client_host.h delete mode 100644 chromium/content/browser/mojo/renderer_capability_filter.cc delete mode 100644 chromium/content/browser/mojo/service_registry_android.cc delete mode 100644 chromium/content/browser/mojo/service_registry_android.h delete mode 100644 chromium/content/browser/navigator_connect/OWNERS delete mode 100644 chromium/content/browser/navigator_connect/navigator_connect_context_impl.cc delete mode 100644 chromium/content/browser/navigator_connect/navigator_connect_context_impl.h delete mode 100644 chromium/content/browser/navigator_connect/service_port_service_impl.cc delete mode 100644 chromium/content/browser/navigator_connect/service_port_service_impl.h delete mode 100644 chromium/content/browser/plugin_loader_posix.cc delete mode 100644 chromium/content/browser/plugin_loader_posix.h delete mode 100644 chromium/content/browser/plugin_loader_posix_unittest.cc delete mode 100644 chromium/content/browser/plugin_process_host.cc delete mode 100644 chromium/content/browser/plugin_process_host.h delete mode 100644 chromium/content/browser/plugin_process_host_mac.cc create mode 100644 chromium/content/browser/renderer_host/browser_compositor_view_mac.h create mode 100644 chromium/content/browser/renderer_host/browser_compositor_view_mac.mm create mode 100644 chromium/content/browser/renderer_host/delegated_frame_host.cc create mode 100644 chromium/content/browser/renderer_host/delegated_frame_host.h delete mode 100644 chromium/content/browser/renderer_host/event_with_latency_info_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/gpu_message_filter.cc delete mode 100644 chromium/content/browser/renderer_host/gpu_message_filter.h create mode 100644 chromium/content/browser/renderer_host/input/mouse_wheel_event_queue.cc create mode 100644 chromium/content/browser/renderer_host/input/mouse_wheel_event_queue.h create mode 100644 chromium/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc create mode 100644 chromium/content/browser/renderer_host/input/non_blocking_event_browsertest.cc create mode 100644 chromium/content/browser/renderer_host/input/web_input_event_builders_android_unittest.cc create mode 100644 chromium/content/browser/renderer_host/media/audio_input_debug_writer_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/memory_benchmark_message_filter.cc delete mode 100644 chromium/content/browser/renderer_host/memory_benchmark_message_filter.h delete mode 100644 chromium/content/browser/renderer_host/p2p/socket_host_unittest.cc create mode 100644 chromium/content/browser/renderer_host/render_widget_host_view_base_observer.cc create mode 100644 chromium/content/browser/renderer_host/render_widget_host_view_base_observer.h create mode 100644 chromium/content/browser/renderer_host/resize_lock.cc create mode 100644 chromium/content/browser/renderer_host/resize_lock.h create mode 100644 chromium/content/browser/renderer_host/websocket_blob_sender.cc create mode 100644 chromium/content/browser/renderer_host/websocket_blob_sender.h create mode 100644 chromium/content/browser/renderer_host/websocket_blob_sender_unittest.cc create mode 100644 chromium/content/browser/screen_orientation/screen_orientation_delegate_win.cc create mode 100644 chromium/content/browser/screen_orientation/screen_orientation_delegate_win.h create mode 100644 chromium/content/browser/service_worker/link_header_support.cc create mode 100644 chromium/content/browser/service_worker/link_header_support.h create mode 100644 chromium/content/browser/service_worker/link_header_support_unittest.cc delete mode 100644 chromium/content/browser/system_message_window_win.cc delete mode 100644 chromium/content/browser/system_message_window_win.h delete mode 100644 chromium/content/browser/system_message_window_win_unittest.cc create mode 100644 chromium/content/browser/top_document_isolation_browsertest.cc delete mode 100644 chromium/content/browser/tracing/battor_power_trace_provider.cc delete mode 100644 chromium/content/browser/tracing/battor_power_trace_provider.h delete mode 100644 chromium/content/browser/udev_linux.cc delete mode 100644 chromium/content/browser/udev_linux.h create mode 100644 chromium/content/browser/zygote_host/zygote_communication_linux.cc create mode 100644 chromium/content/browser/zygote_host/zygote_communication_linux.h create mode 100644 chromium/content/browser/zygote_host/zygote_handle_linux.cc create mode 100644 chromium/content/child/blob_storage/blob_message_filter.cc create mode 100644 chromium/content/child/blob_storage/blob_message_filter.h create mode 100644 chromium/content/child/font_warmup_win.cc create mode 100644 chromium/content/child/font_warmup_win.h create mode 100644 chromium/content/child/font_warmup_win_unittest.cc delete mode 100644 chromium/content/child/multipart_response_delegate.cc delete mode 100644 chromium/content/child/multipart_response_delegate.h delete mode 100644 chromium/content/child/multipart_response_delegate_unittest.cc delete mode 100644 chromium/content/child/navigator_connect/OWNERS delete mode 100644 chromium/content/child/navigator_connect/service_port_dispatcher_impl.cc delete mode 100644 chromium/content/child/navigator_connect/service_port_dispatcher_impl.h delete mode 100644 chromium/content/child/navigator_connect/service_port_provider.cc delete mode 100644 chromium/content/child/navigator_connect/service_port_provider.h create mode 100644 chromium/content/child/notifications/pending_notification.cc create mode 100644 chromium/content/child/notifications/pending_notification.h create mode 100644 chromium/content/child/notifications/pending_notifications_tracker_unittest.cc delete mode 100644 chromium/content/child/npapi/DEPS delete mode 100644 chromium/content/child/npapi/OWNERS delete mode 100644 chromium/content/child/npapi/np_channel_base.cc delete mode 100644 chromium/content/child/npapi/np_channel_base.h delete mode 100644 chromium/content/child/npapi/npobject_base.h delete mode 100644 chromium/content/child/npapi/npobject_proxy.cc delete mode 100644 chromium/content/child/npapi/npobject_proxy.h delete mode 100644 chromium/content/child/npapi/npobject_stub.cc delete mode 100644 chromium/content/child/npapi/npobject_stub.h delete mode 100644 chromium/content/child/npapi/npobject_util.cc delete mode 100644 chromium/content/child/npapi/npobject_util.h delete mode 100644 chromium/content/child/npapi/npruntime_util.cc delete mode 100644 chromium/content/child/npapi/npruntime_util.h delete mode 100644 chromium/content/child/npapi/plugin_host.cc delete mode 100644 chromium/content/child/npapi/plugin_host.h delete mode 100644 chromium/content/child/npapi/plugin_instance.cc delete mode 100644 chromium/content/child/npapi/plugin_instance.h delete mode 100644 chromium/content/child/npapi/plugin_instance_mac.mm delete mode 100644 chromium/content/child/npapi/plugin_lib.cc delete mode 100644 chromium/content/child/npapi/plugin_lib.h delete mode 100644 chromium/content/child/npapi/plugin_lib_unittest.cc delete mode 100644 chromium/content/child/npapi/plugin_web_event_converter_mac.h delete mode 100644 chromium/content/child/npapi/plugin_web_event_converter_mac.mm delete mode 100644 chromium/content/child/npapi/webplugin.h delete mode 100644 chromium/content/child/npapi/webplugin_accelerated_surface_mac.h delete mode 100644 chromium/content/child/npapi/webplugin_delegate.h delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl.cc delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl.h delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl_android.cc delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl_aura.cc delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl_mac.mm delete mode 100644 chromium/content/child/npapi/webplugin_delegate_impl_win.cc delete mode 100644 chromium/content/child/npapi/webplugin_ime_win.cc delete mode 100644 chromium/content/child/npapi/webplugin_ime_win.h delete mode 100644 chromium/content/child/npapi/webplugin_resource_client.h delete mode 100644 chromium/content/child/plugin_message_generator.cc delete mode 100644 chromium/content/child/plugin_message_generator.h delete mode 100644 chromium/content/child/plugin_messages.h delete mode 100644 chromium/content/child/plugin_param_traits.cc delete mode 100644 chromium/content/child/plugin_param_traits.h create mode 100644 chromium/content/child/storage_util.cc create mode 100644 chromium/content/child/storage_util.h delete mode 100644 chromium/content/child/threaded_data_provider.cc delete mode 100644 chromium/content/child/threaded_data_provider.h delete mode 100644 chromium/content/child/web_discardable_memory_impl.cc delete mode 100644 chromium/content/child/web_discardable_memory_impl.h delete mode 100644 chromium/content/child/web_memory_allocator_dump_impl.cc delete mode 100644 chromium/content/child/web_memory_allocator_dump_impl.h delete mode 100644 chromium/content/child/web_memory_dump_provider_adapter.cc delete mode 100644 chromium/content/child/web_memory_dump_provider_adapter.h delete mode 100644 chromium/content/child/web_process_memory_dump_impl.cc delete mode 100644 chromium/content/child/web_process_memory_dump_impl.h delete mode 100644 chromium/content/child/web_process_memory_dump_impl_unittest.cc create mode 100644 chromium/content/common/accelerated_surface_buffers_swapped_params_mac.cc create mode 100644 chromium/content/common/accelerated_surface_buffers_swapped_params_mac.h create mode 100644 chromium/content/common/android/media_metadata_android.cc create mode 100644 chromium/content/common/android/media_metadata_android.h delete mode 100644 chromium/content/common/android/surface_texture_manager.cc delete mode 100644 chromium/content/common/android/surface_texture_manager.h delete mode 100644 chromium/content/common/android/surface_texture_peer.cc delete mode 100644 chromium/content/common/android/surface_texture_peer.h create mode 100644 chromium/content/common/buffer_presented_params_mac.cc create mode 100644 chromium/content/common/buffer_presented_params_mac.h delete mode 100644 chromium/content/common/dwrite_font_platform_win.cc delete mode 100644 chromium/content/common/dwrite_font_platform_win_unittest.cc create mode 100644 chromium/content/common/establish_channel_params.cc create mode 100644 chromium/content/common/establish_channel_params.h delete mode 100644 chromium/content/common/experiments/api_key.cc delete mode 100644 chromium/content/common/experiments/api_key.h delete mode 100644 chromium/content/common/experiments/api_key_unittest.cc delete mode 100644 chromium/content/common/font_warmup_win.cc delete mode 100644 chromium/content/common/font_warmup_win.h delete mode 100644 chromium/content/common/font_warmup_win_unittest.cc delete mode 100644 chromium/content/common/frame_param.cc delete mode 100644 chromium/content/common/frame_param.h delete mode 100644 chromium/content/common/frame_param_macros.h delete mode 100644 chromium/content/common/geolocation_service.mojom delete mode 100644 chromium/content/common/gpu/ca_layer_partial_damage_tree_mac.h delete mode 100644 chromium/content/common/gpu/ca_layer_partial_damage_tree_mac.mm delete mode 100644 chromium/content/common/gpu/ca_layer_tree_mac.h delete mode 100644 chromium/content/common/gpu/ca_layer_tree_mac.mm delete mode 100644 chromium/content/common/gpu/child_window_surface_win.cc delete mode 100644 chromium/content/common/gpu/child_window_surface_win.h delete mode 100644 chromium/content/common/gpu/client/command_buffer_proxy_impl.cc delete mode 100644 chromium/content/common/gpu/client/command_buffer_proxy_impl.h delete mode 100644 chromium/content/common/gpu/client/gl_helper.cc delete mode 100644 chromium/content/common/gpu/client/gl_helper.h delete mode 100644 chromium/content/common/gpu/client/gl_helper_benchmark.cc delete mode 100644 chromium/content/common/gpu/client/gl_helper_readback_support.cc delete mode 100644 chromium/content/common/gpu/client/gl_helper_readback_support.h delete mode 100644 chromium/content/common/gpu/client/gl_helper_scaling.cc delete mode 100644 chromium/content/common/gpu/client/gl_helper_scaling.h delete mode 100644 chromium/content/common/gpu/client/gl_helper_unittest.cc delete mode 100644 chromium/content/common/gpu/client/gpu_channel_host.cc delete mode 100644 chromium/content/common/gpu/client/gpu_channel_host.h delete mode 100644 chromium/content/common/gpu/client/gpu_jpeg_decode_accelerator_host.cc delete mode 100644 chromium/content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl.h delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.cc delete mode 100644 chromium/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h delete mode 100644 chromium/content/common/gpu/client/gpu_video_decode_accelerator_host.cc delete mode 100644 chromium/content/common/gpu/client/gpu_video_decode_accelerator_host.h delete mode 100644 chromium/content/common/gpu/client/gpu_video_encode_accelerator_host.cc delete mode 100644 chromium/content/common/gpu/client/gpu_video_encode_accelerator_host.h create mode 100644 chromium/content/common/gpu/client/grcontext_for_gles2_interface.cc create mode 100644 chromium/content/common/gpu/client/grcontext_for_gles2_interface.h delete mode 100644 chromium/content/common/gpu/client/grcontext_for_webgraphicscontext3d.cc delete mode 100644 chromium/content/common/gpu/client/grcontext_for_webgraphicscontext3d.h delete mode 100644 chromium/content/common/gpu/gpu_channel.cc delete mode 100644 chromium/content/common/gpu/gpu_channel.h delete mode 100644 chromium/content/common/gpu/gpu_channel_manager.cc delete mode 100644 chromium/content/common/gpu/gpu_channel_manager.h delete mode 100644 chromium/content/common/gpu/gpu_channel_manager_unittest.cc delete mode 100644 chromium/content/common/gpu/gpu_channel_test_common.cc delete mode 100644 chromium/content/common/gpu/gpu_channel_test_common.h delete mode 100644 chromium/content/common/gpu/gpu_channel_unittest.cc delete mode 100644 chromium/content/common/gpu/gpu_command_buffer_stub.cc delete mode 100644 chromium/content/common/gpu/gpu_command_buffer_stub.h delete mode 100644 chromium/content/common/gpu/gpu_config.h delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory.h delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_io_surface.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_io_surface.h delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_io_surface_unittest.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.h delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap_unittest.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_surface_texture.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_surface_texture.h delete mode 100644 chromium/content/common/gpu/gpu_memory_buffer_factory_surface_texture_unittest.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_manager.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_manager.h delete mode 100644 chromium/content/common/gpu/gpu_memory_tracking.cc delete mode 100644 chromium/content/common/gpu/gpu_memory_tracking.h delete mode 100644 chromium/content/common/gpu/gpu_memory_uma_stats.h delete mode 100644 chromium/content/common/gpu/gpu_messages.h delete mode 100644 chromium/content/common/gpu/gpu_process_launch_causes.h delete mode 100644 chromium/content/common/gpu/gpu_result_codes.h delete mode 100644 chromium/content/common/gpu/gpu_stream_priority.h delete mode 100644 chromium/content/common/gpu/gpu_surface_lookup.cc delete mode 100644 chromium/content/common/gpu/gpu_surface_lookup.h delete mode 100644 chromium/content/common/gpu/gpu_watchdog.h delete mode 100644 chromium/content/common/gpu/image_transport_surface.cc delete mode 100644 chromium/content/common/gpu/image_transport_surface.h delete mode 100644 chromium/content/common/gpu/image_transport_surface_android.cc delete mode 100644 chromium/content/common/gpu/image_transport_surface_linux.cc delete mode 100644 chromium/content/common/gpu/image_transport_surface_mac.mm delete mode 100644 chromium/content/common/gpu/image_transport_surface_overlay_mac.h delete mode 100644 chromium/content/common/gpu/image_transport_surface_overlay_mac.mm delete mode 100644 chromium/content/common/gpu/image_transport_surface_win.cc delete mode 100644 chromium/content/common/gpu/media/gpu_arc_video_service.cc delete mode 100644 chromium/content/common/gpu/media/gpu_arc_video_service.h delete mode 100644 chromium/content/common/gpu/media/gpu_video_accelerator_util.cc delete mode 100644 chromium/content/common/gpu/media/gpu_video_accelerator_util.h create mode 100644 chromium/content/common/gpu/media/gpu_video_decode_accelerator_factory_impl.cc create mode 100644 chromium/content/common/gpu/media/gpu_video_decode_accelerator_factory_impl.h create mode 100644 chromium/content/common/gpu/media/gpu_video_decode_accelerator_helpers.h create mode 100644 chromium/content/common/gpu/media/media_channel.cc create mode 100644 chromium/content/common/gpu/media/media_channel.h create mode 100644 chromium/content/common/gpu/media/media_service.cc create mode 100644 chromium/content/common/gpu/media/media_service.h create mode 100644 chromium/content/common/gpu/media/shared_memory_region.cc create mode 100644 chromium/content/common/gpu/media/shared_memory_region.h create mode 100644 chromium/content/common/gpu/media/vt_video_encode_accelerator_mac.cc create mode 100644 chromium/content/common/gpu/media/vt_video_encode_accelerator_mac.h delete mode 100644 chromium/content/common/gpu/stream_texture_android.cc delete mode 100644 chromium/content/common/gpu/stream_texture_android.h delete mode 100644 chromium/content/common/gpu/x_util.h create mode 100644 chromium/content/common/gpu_host_messages.h create mode 100644 chromium/content/common/gpu_process_launch_causes.h delete mode 100644 chromium/content/common/id_type.h delete mode 100644 chromium/content/common/id_type_unittest.cc create mode 100644 chromium/content/common/input/event_with_latency_info.h create mode 100644 chromium/content/common/input/event_with_latency_info_unittest.cc create mode 100644 chromium/content/common/input/input_event_dispatch_type.h create mode 100644 chromium/content/common/input/input_event_utils.cc create mode 100644 chromium/content/common/input/input_event_utils.h create mode 100644 chromium/content/common/input/synthetic_pointer_action_params.cc create mode 100644 chromium/content/common/input/synthetic_pointer_action_params.h create mode 100644 chromium/content/common/input/web_input_event_queue.h create mode 100644 chromium/content/common/leveldb_wrapper.mojom create mode 100644 chromium/content/common/media/media_player_delegate_messages.h create mode 100644 chromium/content/common/media/surface_view_manager_messages_android.h delete mode 100644 chromium/content/common/memory_benchmark_messages.h delete mode 100644 chromium/content/common/message_router.cc delete mode 100644 chromium/content/common/message_router.h create mode 100644 chromium/content/common/mojo/current_thread_loader.cc create mode 100644 chromium/content/common/mojo/current_thread_loader.h create mode 100644 chromium/content/common/mojo/static_loader.cc create mode 100644 chromium/content/common/mojo/static_loader.h create mode 100644 chromium/content/common/net/url_request_service_worker_data.cc create mode 100644 chromium/content/common/net/url_request_service_worker_data.h create mode 100644 chromium/content/common/origin_trials/trial_token.cc create mode 100644 chromium/content/common/origin_trials/trial_token.h create mode 100644 chromium/content/common/origin_trials/trial_token_unittest.cc create mode 100644 chromium/content/common/origin_trials/trial_token_validator.cc create mode 100644 chromium/content/common/origin_trials/trial_token_validator.h create mode 100644 chromium/content/common/origin_trials/trial_token_validator_unittest.cc create mode 100644 chromium/content/common/page_messages.h delete mode 100644 chromium/content/common/permission_service.mojom delete mode 100644 chromium/content/common/plugin_constants_win.cc delete mode 100644 chromium/content/common/plugin_constants_win.h delete mode 100644 chromium/content/common/plugin_list_mac.mm delete mode 100644 chromium/content/common/plugin_list_posix.cc delete mode 100644 chromium/content/common/plugin_list_win.cc delete mode 100644 chromium/content/common/plugin_process_messages.h create mode 100644 chromium/content/common/resize_params.cc create mode 100644 chromium/content/common/resize_params.h delete mode 100644 chromium/content/common/service_port_service.mojom delete mode 100644 chromium/content/common/service_port_type_converters.cc delete mode 100644 chromium/content/common/service_port_type_converters.h create mode 100644 chromium/content/common/service_worker/embedded_worker_settings.h create mode 100644 chromium/content/common/storage_partition_service.mojom delete mode 100644 chromium/content/common/webplugin_geometry.cc delete mode 100644 chromium/content/common/webplugin_geometry.h delete mode 100644 chromium/content/content.gni delete mode 100644 chromium/content/content_plugin.gypi create mode 100644 chromium/content/content_site_isolation_browsertests.isolate delete mode 100644 chromium/content/plugin/BUILD.gn delete mode 100644 chromium/content/plugin/DEPS delete mode 100644 chromium/content/plugin/OWNERS delete mode 100644 chromium/content/plugin/plugin_channel.cc delete mode 100644 chromium/content/plugin/plugin_channel.h delete mode 100644 chromium/content/plugin/plugin_interpose_util_mac.h delete mode 100644 chromium/content/plugin/plugin_interpose_util_mac.mm delete mode 100644 chromium/content/plugin/plugin_main.cc delete mode 100644 chromium/content/plugin/plugin_main_mac.mm delete mode 100644 chromium/content/plugin/plugin_thread.cc delete mode 100644 chromium/content/plugin/plugin_thread.h delete mode 100644 chromium/content/plugin/webplugin_accelerated_surface_proxy_mac.cc delete mode 100644 chromium/content/plugin/webplugin_accelerated_surface_proxy_mac.h delete mode 100644 chromium/content/plugin/webplugin_delegate_stub.cc delete mode 100644 chromium/content/plugin/webplugin_delegate_stub.h delete mode 100644 chromium/content/plugin/webplugin_proxy.cc delete mode 100644 chromium/content/plugin/webplugin_proxy.h create mode 100644 chromium/content/public/app/mojo/content_browser_manifest.json create mode 100644 chromium/content/public/app/mojo/content_renderer_manifest.json create mode 100644 chromium/content/public/browser/android/service_registry_android.h create mode 100644 chromium/content/public/browser/android/synchronous_compositor.cc delete mode 100644 chromium/content/public/browser/arc_video_host_delegate.h delete mode 100644 chromium/content/public/browser/download_destination_observer.h create mode 100644 chromium/content/public/browser/gpu_service_registry.cc create mode 100644 chromium/content/public/browser/gpu_service_registry.h create mode 100644 chromium/content/public/browser/gpu_utils.cc create mode 100644 chromium/content/public/browser/gpu_utils.h create mode 100644 chromium/content/public/browser/memory_pressure_controller.cc create mode 100644 chromium/content/public/browser/memory_pressure_controller.h delete mode 100644 chromium/content/public/browser/navigator_connect_context.h delete mode 100644 chromium/content/public/browser/navigator_connect_service_factory.h create mode 100644 chromium/content/public/browser/resource_dispatcher_host.cc create mode 100644 chromium/content/public/browser/zygote_handle_linux.h delete mode 100644 chromium/content/public/common/dwrite_font_platform_win.h create mode 100644 chromium/content/public/common/feature_h264_with_openh264_ffmpeg.cc create mode 100644 chromium/content/public/common/feature_h264_with_openh264_ffmpeg.h delete mode 100644 chromium/content/public/common/gpu_memory_stats.cc delete mode 100644 chromium/content/public/common/gpu_memory_stats.h create mode 100644 chromium/content/public/common/media_metadata.cc create mode 100644 chromium/content/public/common/media_metadata.h delete mode 100644 chromium/content/public/common/mojo_geoposition.mojom delete mode 100644 chromium/content/public/common/navigator_connect_client.cc delete mode 100644 chromium/content/public/common/navigator_connect_client.h create mode 100644 chromium/content/public/common/notification_resources.cc create mode 100644 chromium/content/public/common/notification_resources.h delete mode 100644 chromium/content/public/common/permission_status.mojom create mode 100644 chromium/content/public/common/push_event_payload.h create mode 100644 chromium/content/public/common/push_subscription_options.h create mode 100644 chromium/content/public/common/zygote_handle.h create mode 100644 chromium/content/public/gpu/BUILD.gn create mode 100644 chromium/content/public/gpu/DEPS create mode 100644 chromium/content/public/gpu/content_gpu_client.h create mode 100644 chromium/content/public/gpu/gpu_video_decode_accelerator_factory.cc create mode 100644 chromium/content/public/gpu/gpu_video_decode_accelerator_factory.h delete mode 100644 chromium/content/public/plugin/BUILD.gn delete mode 100644 chromium/content/public/plugin/content_plugin_client.h delete mode 100644 chromium/content/public/renderer/media_stream_api.cc delete mode 100644 chromium/content/public/renderer/media_stream_api.h create mode 100644 chromium/content/public/renderer/media_stream_utils.cc create mode 100644 chromium/content/public/renderer/media_stream_utils.h delete mode 100644 chromium/content/renderer/battery_status/OWNERS delete mode 100644 chromium/content/renderer/battery_status/battery_status_dispatcher.cc delete mode 100644 chromium/content/renderer/battery_status/battery_status_dispatcher.h delete mode 100644 chromium/content/renderer/battery_status/battery_status_dispatcher_unittest.cc create mode 100644 chromium/content/renderer/devtools/render_widget_screen_metrics_emulator.cc create mode 100644 chromium/content/renderer/devtools/render_widget_screen_metrics_emulator.h create mode 100644 chromium/content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h create mode 100644 chromium/content/renderer/dom_storage/DEPS create mode 100644 chromium/content/renderer/dom_storage/local_storage_area.cc create mode 100644 chromium/content/renderer/dom_storage/local_storage_area.h create mode 100644 chromium/content/renderer/dom_storage/local_storage_cached_area.cc create mode 100644 chromium/content/renderer/dom_storage/local_storage_cached_area.h create mode 100644 chromium/content/renderer/dom_storage/local_storage_cached_areas.cc create mode 100644 chromium/content/renderer/dom_storage/local_storage_cached_areas.h create mode 100644 chromium/content/renderer/dom_storage/local_storage_namespace.cc create mode 100644 chromium/content/renderer/dom_storage/local_storage_namespace.h create mode 100644 chromium/content/renderer/frame_blame_context.cc create mode 100644 chromium/content/renderer/frame_blame_context.h create mode 100644 chromium/content/renderer/gpu/render_widget_compositor_delegate.h create mode 100644 chromium/content/renderer/input/main_thread_event_queue.cc create mode 100644 chromium/content/renderer/input/main_thread_event_queue.h create mode 100644 chromium/content/renderer/input/main_thread_event_queue_unittest.cc create mode 100644 chromium/content/renderer/media/android/renderer_surface_view_manager.cc create mode 100644 chromium/content/renderer/media/android/renderer_surface_view_manager.h create mode 100644 chromium/content/renderer/media/media_interface_provider.cc create mode 100644 chromium/content/renderer/media/media_interface_provider.h delete mode 100644 chromium/content/renderer/media/media_permission_dispatcher_impl.cc delete mode 100644 chromium/content/renderer/media/media_permission_dispatcher_impl.h delete mode 100644 chromium/content/renderer/media/media_permission_dispatcher_proxy.cc delete mode 100644 chromium/content/renderer/media/media_permission_dispatcher_proxy.h create mode 100644 chromium/content/renderer/media/mock_constraint_factory.cc create mode 100644 chromium/content/renderer/media/mock_constraint_factory.h delete mode 100644 chromium/content/renderer/media/mock_media_constraint_factory.cc delete mode 100644 chromium/content/renderer/media/mock_media_constraint_factory.h create mode 100644 chromium/content/renderer/media/peer_connection_tracker_unittest.cc create mode 100644 chromium/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc delete mode 100644 chromium/content/renderer/media/rtc_media_constraints.cc delete mode 100644 chromium/content/renderer/media/rtc_media_constraints.h create mode 100644 chromium/content/renderer/media/track_audio_renderer.cc create mode 100644 chromium/content/renderer/media/track_audio_renderer.h delete mode 100644 chromium/content/renderer/media/webrtc_local_audio_renderer.cc delete mode 100644 chromium/content/renderer/media/webrtc_local_audio_renderer.h delete mode 100644 chromium/content/renderer/memory_benchmarking_extension.cc delete mode 100644 chromium/content/renderer/memory_benchmarking_extension.h create mode 100644 chromium/content/renderer/mojo/blink_service_registry_impl.cc create mode 100644 chromium/content/renderer/mojo/blink_service_registry_impl.h create mode 100644 chromium/content/renderer/mus/compositor_mus_connection_unittest.cc delete mode 100644 chromium/content/renderer/npapi/plugin_channel_host.cc delete mode 100644 chromium/content/renderer/npapi/plugin_channel_host.h delete mode 100644 chromium/content/renderer/npapi/webplugin_delegate_proxy.cc delete mode 100644 chromium/content/renderer/npapi/webplugin_delegate_proxy.h delete mode 100644 chromium/content/renderer/npapi/webplugin_impl.cc delete mode 100644 chromium/content/renderer/npapi/webplugin_impl.h delete mode 100644 chromium/content/renderer/npapi/webplugin_impl_unittest.cc create mode 100644 chromium/content/renderer/origin_trials/web_trial_token_validator_impl.cc create mode 100644 chromium/content/renderer/origin_trials/web_trial_token_validator_impl.h create mode 100644 chromium/content/renderer/render_widget_owner_delegate.h create mode 100644 chromium/content/renderer/top_level_blame_context.cc create mode 100644 chromium/content/renderer/top_level_blame_context.h create mode 100644 chromium/content/shell/common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom create mode 100644 chromium/content/shell/common/layout_test/layout_test_content_client.cc create mode 100644 chromium/content/shell/common/layout_test/layout_test_content_client.h create mode 100644 chromium/content/shell/common/layout_test/layout_test_switches.cc create mode 100644 chromium/content/shell/common/layout_test/layout_test_switches.h create mode 100644 chromium/content/shell/shell_resources.grd (limited to 'chromium/content') diff --git a/chromium/content/BUILD.gn b/chromium/content/BUILD.gn index cb50de5f703..56941b8f427 100644 --- a/chromium/content/BUILD.gn +++ b/chromium/content/BUILD.gn @@ -37,14 +37,31 @@ config("content_implementation") { # //content/public/browser and similar targets to avoid double-linking (these # targets make sure the dependency goes through the content shared library # when doing a component build). +# +# TESTS +# ----- +# Tests are a challenge. The content tests need to access internals of +# content/browser, for example, but the tests themselves are outside of the +# content component (which is a shared library in the component build). To +# prevent external-to-content targets from depending on private headers, +# the non-public content/browser target is not a public dep of the content +# component. But this means there is also no public path for the content +# tests and no way to restrict that just to //content/test/* if we added one. +# +# As a result, we check deps for //content/test/* only in non-component builds +# (which should verify the dependencies are correct for both component and +# non-component cases equally). There are targets like +# //content/browser:for_content_tests that allow content/test to depend on the +# //content/browser target only in non-component builds (when there are no +# linking problems) for when check is enabled. if (!is_nacl_nonsfi) { content_shared_components = [ "//content/gpu:gpu_sources", "//content/public/browser:browser_sources", "//content/public/child:child_sources", + "//content/public/gpu:gpu_sources", "//content/public/common:common_sources", - "//content/public/plugin:plugin_sources", "//content/public/renderer:renderer_sources", "//content/public/utility:utility_sources", ] @@ -93,11 +110,21 @@ grit("resources") { "grit/content_resources.h", "content_resources.pak", ] + grit_flags = [ + "-E", + "root_out_dir=" + rebase_path(root_out_dir, root_build_dir), + ] + deps = [ + "//content/public/app:browser_manifest", + "//content/public/app:renderer_manifest", + "//mojo/services/catalog:manifest", + ] } # This target exists to "hold" the content_export header so we can do proper # inclusion testing of it. source_set("export") { + # Must only be used inside of content. visibility = [ "//content/*" ] sources = [ "common/content_export.h", diff --git a/chromium/content/DEPS b/chromium/content/DEPS index e62959c7e43..531e5edaa59 100644 --- a/chromium/content/DEPS +++ b/chromium/content/DEPS @@ -10,14 +10,8 @@ include_rules = [ "+content/grit", "+content/public/common", "+content/public/test", - "+content/shell", # for content_browsertests "+content/test", - # content isn't tied to prefs so that other embedders are able to pick - # different ways of storing their preferences. Also, this is to avoid prefs - # being used as a parallel API to the Content API. - "-base/prefs", - "+blink", "+cc", @@ -34,6 +28,9 @@ include_rules = [ "+dbus", "+gpu", "+mojo/common", + "+mojo/edk/embedder", + "+mojo/edk/js", + "+mojo/edk/test", "+mojo/message_pump", "+mojo/public", "+net", @@ -62,16 +59,11 @@ include_rules = [ "+third_party/angle", "+third_party/flac", "+third_party/libjingle", - "+third_party/mojo/src/mojo/edk/embedder", - "+third_party/mojo/src/mojo/edk/js", - "+third_party/mojo/src/mojo/edk/test", "+third_party/mozilla", - "+third_party/npapi/bindings", "+third_party/ocmock", "+third_party/re2", "+third_party/skia", "+third_party/sqlite", - "+third_party/tcmalloc", "+third_party/khronos", "+third_party/webrtc", "+third_party/zlib/google", @@ -85,6 +77,7 @@ include_rules = [ "+ui/aura", "+ui/base", "+ui/compositor", + "+ui/display", "+ui/events", "+ui/gfx", "+ui/gl", @@ -118,3 +111,12 @@ include_rules = [ # For generated JNI includes. "+jni", ] + +# content -> content/shell dependency is not allowed, except for browser tests. +specific_include_rules = { + ".*_browsertest[a-z_]*\.cc": [ + "+content/shell/browser", + "+content/shell/common", + ], +} + diff --git a/chromium/content/OWNERS b/chromium/content/OWNERS index cf7197c2f46..2d7b39db9f0 100644 --- a/chromium/content/OWNERS +++ b/chromium/content/OWNERS @@ -1,7 +1,6 @@ avi@chromium.org creis@chromium.org darin@chromium.org -davidben@chromium.org jam@chromium.org jochen@chromium.org kinuko@chromium.org diff --git a/chromium/content/README b/chromium/content/README deleted file mode 100644 index 930fedfe25c..00000000000 --- a/chromium/content/README +++ /dev/null @@ -1,6 +0,0 @@ -Content is the core code needed to render a page using a multi-process sandboxed -browser. - -Developer documentation: -http://www.chromium.org/developers/content-module - diff --git a/chromium/content/README.md b/chromium/content/README.md new file mode 100644 index 00000000000..984f02a19b0 --- /dev/null +++ b/chromium/content/README.md @@ -0,0 +1,11 @@ +Content is the core code needed to render a page using a multi-process sandboxed +browser. + +Developer documentation: +http://www.chromium.org/developers/content-module + +Internal Documentation: + +* [Bluetooth](browser/bluetooth/README.md) + + diff --git a/chromium/content/app/BUILD.gn b/chromium/content/app/BUILD.gn index 6abee2318b4..149143920f6 100644 --- a/chromium/content/app/BUILD.gn +++ b/chromium/content/app/BUILD.gn @@ -3,7 +3,6 @@ # found in the LICENSE file. import("//build/config/chrome_build.gni") -import("//build/config/ui.gni") content_app_sources = [ "android/app_jni_registrar.cc", @@ -13,6 +12,7 @@ content_app_sources = [ "android/content_jni_onload.cc", "android/content_main.cc", "android/content_main.h", + "android/download_main.cc", "android/library_loader_hooks.cc", "android/library_loader_hooks.h", "content_main.cc", @@ -26,16 +26,13 @@ content_app_sources = [ content_app_deps = [ "//base", "//base:i18n", - - # This is needed by app/content_main_runner.cc - # TODO(brettw) this shouldn't be here, only final executables should be - # picking the allocator. http://crbug.com/571731 - "//base/allocator", "//content:export", "//content:sandbox_helper_win", "//content/public/common:common_sources", "//content/public/common:mojo_bindings", "//crypto", + "//mojo/shell/public/interfaces", + "//mojo/edk/system", "//ui/base", "//ui/gfx", "//ui/gfx/geometry", @@ -47,26 +44,11 @@ if (is_win) { content_app_sources -= [ "content_main.cc" ] content_app_deps += [ "//content/public/android:jni", + "//device/usb", "//device/vibration", "//skia", "//third_party/android_tools:cpu_features", - ] - if (!use_aura) { - content_app_deps += [ "//ui/android" ] - } -} - -if (is_ios) { - content_app_sources -= [ - "content_main.cc", - "mojo/mojo_init.cc", - "mojo/mojo_init.h", - ] -} else { - content_app_deps += [ - "//mojo/shell/public/interfaces", - "//mojo/environment:chromium", - "//third_party/mojo/src/mojo/edk/system", + "//ui/android", ] } @@ -85,13 +67,26 @@ if (!is_multi_dll_chrome) { source_set("both") { # Only the public target should depend on this. All other targets (even # internal content ones) should depend on the public one. - visibility = [ "//content/public/app:*" ] + visibility = [ + ":both_for_content_tests", # See top of //content/BUILD.gn for why. + "//content/public/app:*", + ] sources = content_app_sources configs += content_app_extra_configs deps = content_app_deps } +# See comment at the top of //content/BUILD.gn for how this works. +group("both_for_content_tests") { + visibility = [ "//content/test/*" ] + if (!is_component_build) { + public_deps = [ + ":both", + ] + } +} + if (is_multi_dll_chrome) { # It doesn't make sense to do the browser/child dll split in component mode. assert(!is_component_build) diff --git a/chromium/content/app/DEPS b/chromium/content/app/DEPS index 774523882ac..550e9a6303f 100644 --- a/chromium/content/app/DEPS +++ b/chromium/content/app/DEPS @@ -10,5 +10,6 @@ include_rules = [ "+gin/public/snapshot_fd_data.h", "+gin/v8_initializer.h", "+media/base", # For initializing media library. - "+media/midi", # For initializing midi library. + "+media/capture/video/android/capture_jni_registrar.h", + "+media/midi/midi_jni_registrar.h", ] diff --git a/chromium/content/app/android/child_process_service.cc b/chromium/content/app/android/child_process_service.cc index 3627eba8877..e44974535dd 100644 --- a/chromium/content/app/android/child_process_service.cc +++ b/chromium/content/app/android/child_process_service.cc @@ -14,10 +14,10 @@ #include "base/macros.h" #include "base/posix/global_descriptors.h" #include "content/child/child_thread_impl.h" -#include "content/common/android/surface_texture_manager.h" -#include "content/common/android/surface_texture_peer.h" -#include "content/common/gpu/gpu_surface_lookup.h" #include "content/public/common/content_descriptors.h" +#include "gpu/ipc/common/android/surface_texture_manager.h" +#include "gpu/ipc/common/android/surface_texture_peer.h" +#include "gpu/ipc/common/gpu_surface_lookup.h" #include "ipc/ipc_descriptors.h" #include "jni/ChildProcessService_jni.h" #include "ui/gl/android/scoped_java_surface.h" @@ -32,9 +32,9 @@ namespace { // TODO(sievers): Use two different implementations of this depending on if // we're in a renderer or gpu process. -class SurfaceTextureManagerImpl : public SurfaceTextureManager, - public SurfaceTexturePeer, - public GpuSurfaceLookup { +class SurfaceTextureManagerImpl : public gpu::SurfaceTextureManager, + public gpu::SurfaceTexturePeer, + public gpu::GpuSurfaceLookup { public: // |service| is the instance of // org.chromium.content.app.ChildProcessService. @@ -42,11 +42,11 @@ class SurfaceTextureManagerImpl : public SurfaceTextureManager, const base::android::JavaRef& service) : service_(service) { SurfaceTexturePeer::InitInstance(this); - GpuSurfaceLookup::InitInstance(this); + gpu::GpuSurfaceLookup::InitInstance(this); } ~SurfaceTextureManagerImpl() override { SurfaceTexturePeer::InitInstance(NULL); - GpuSurfaceLookup::InitInstance(NULL); + gpu::GpuSurfaceLookup::InitInstance(NULL); } // Overridden from SurfaceTextureManager: @@ -146,7 +146,8 @@ void InternalInitChildProcess(JNIEnv* env, jlong cpu_features) { // Set the CPU properties. android_setCpu(cpu_count, cpu_features); - SurfaceTextureManager::SetInstance(new SurfaceTextureManagerImpl(service)); + gpu::SurfaceTextureManager::SetInstance( + new SurfaceTextureManagerImpl(service)); base::android::MemoryPressureListenerAndroid::RegisterSystemCallback(env); } diff --git a/chromium/content/app/android/content_main.cc b/chromium/content/app/android/content_main.cc index 4eaa3a3375b..fa4c781b542 100644 --- a/chromium/content/app/android/content_main.cc +++ b/chromium/content/app/android/content_main.cc @@ -8,6 +8,7 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" #include "base/trace_event/trace_event.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_delegate.h" diff --git a/chromium/content/app/android/download_main.cc b/chromium/content/app/android/download_main.cc new file mode 100644 index 00000000000..3280e29a3cc --- /dev/null +++ b/chromium/content/app/android/download_main.cc @@ -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. + +#include "base/message_loop/message_loop.h" +#include "base/threading/platform_thread.h" +#include "content/public/common/main_function_params.h" + +namespace content { + +// Mainline routine for running as the download process. +int DownloadMain(const MainFunctionParams& parameters) { + // The main message loop of the utility process. + base::MessageLoop main_message_loop; + base::PlatformThread::SetName("CrDownloadMain"); + base::MessageLoop::current()->Run(); + + return 0; +} + +} // namespace content diff --git a/chromium/content/app/android/library_loader_hooks.cc b/chromium/content/app/android/library_loader_hooks.cc index 21e51b36b92..699d7e8e9b9 100644 --- a/chromium/content/app/android/library_loader_hooks.cc +++ b/chromium/content/app/android/library_loader_hooks.cc @@ -28,6 +28,7 @@ #include "device/bluetooth/android/bluetooth_jni_registrar.h" #include "device/usb/android/usb_jni_registrar.h" #include "media/base/android/media_jni_registrar.h" +#include "media/capture/video/android/capture_jni_registrar.h" #include "media/midi/midi_jni_registrar.h" #include "net/android/net_jni_registrar.h" #include "ui/android/ui_android_jni_registrar.h" @@ -35,10 +36,7 @@ #include "ui/events/android/events_jni_registrar.h" #include "ui/gfx/android/gfx_jni_registrar.h" #include "ui/gl/android/gl_jni_registrar.h" - -#if !defined(USE_AURA) #include "ui/shell_dialogs/android/shell_dialogs_jni_registrar.h" -#endif namespace content { @@ -64,6 +62,9 @@ bool EnsureJniRegistered(JNIEnv* env) { if (!ui::events::android::RegisterJni(env)) return false; + if (!ui::shell_dialogs::RegisterJni(env)) + return false; + if (!content::android::RegisterCommonJni(env)) return false; @@ -82,16 +83,14 @@ bool EnsureJniRegistered(JNIEnv* env) { if (!media::RegisterJni(env)) return false; - if (!media::midi::RegisterJni(env)) + if (!media::RegisterCaptureJni(env)) return false; -#if !defined(USE_AURA) - if (!ui::shell_dialogs::RegisterJni(env)) + if (!media::midi::RegisterJni(env)) return false; if (!ui::RegisterUIAndroidJni(env)) return false; -#endif g_jni_init_done = true; } diff --git a/chromium/content/app/content_main_runner.cc b/chromium/content/app/content_main_runner.cc index 037c79bd512..209dc90496e 100644 --- a/chromium/content/app/content_main_runner.cc +++ b/chromium/content/app/content_main_runner.cc @@ -13,9 +13,11 @@ #include "base/allocator/allocator_check.h" #include "base/allocator/allocator_extension.h" #include "base/at_exit.h" +#include "base/base_switches.h" #include "base/command_line.h" #include "base/debug/debugger.h" #include "base/debug/stack_trace.h" +#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/i18n/icu_util.h" #include "base/lazy_instance.h" @@ -23,12 +25,13 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/histogram_base.h" #include "base/metrics/statistics_recorder.h" #include "base/path_service.h" #include "base/process/launch.h" #include "base/process/memory.h" #include "base/process/process_handle.h" -#include "base/profiler/alternate_timer.h" #include "base/profiler/scoped_tracker.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -38,7 +41,11 @@ #include "components/tracing/trace_config_file.h" #include "components/tracing/trace_to_console.h" #include "components/tracing/tracing_switches.h" +#include "content/app/mojo/mojo_init.h" #include "content/browser/browser_main.h" +#include "content/browser/gpu/gpu_process_host.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_process_host_impl.h" #include "content/common/set_process_title.h" #include "content/common/url_schemes.h" #include "content/gpu/in_process_gpu_thread.h" @@ -51,6 +58,9 @@ #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/common/sandbox_init.h" +#include "content/public/gpu/content_gpu_client.h" +#include "content/public/renderer/content_renderer_client.h" +#include "content/public/utility/content_utility_client.h" #include "content/renderer/in_process_renderer_thread.h" #include "content/utility/in_process_utility_thread.h" #include "ipc/ipc_descriptors.h" @@ -64,20 +74,6 @@ #include "gin/v8_initializer.h" #endif -#if defined(USE_TCMALLOC) -#include "third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h" -#endif - -#if !defined(OS_IOS) -#include "content/app/mojo/mojo_init.h" -#include "content/browser/gpu/gpu_process_host.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/utility_process_host_impl.h" -#include "content/public/plugin/content_plugin_client.h" -#include "content/public/renderer/content_renderer_client.h" -#include "content/public/utility/content_utility_client.h" -#endif - #if defined(OS_WIN) #include #include @@ -88,12 +84,10 @@ #include "ui/gfx/win/dpi.h" #elif defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" -#if !defined(OS_IOS) #include "base/power_monitor/power_monitor_device_source.h" #include "content/app/mac/mac_init.h" #include "content/browser/mach_broker_mac.h" #include "content/common/sandbox_init_mac.h" -#endif // !OS_IOS #endif // OS_WIN #if defined(OS_POSIX) @@ -115,12 +109,6 @@ #include "crypto/nss_util.h" #endif -#if !defined(OS_MACOSX) && defined(USE_TCMALLOC) -extern "C" { -int tc_set_new_mode(int mode); -} -#endif - namespace content { extern int GpuMain(const content::MainFunctionParams&); #if defined(ENABLE_PLUGINS) @@ -132,29 +120,74 @@ extern int PpapiBrokerMain(const MainFunctionParams&); #endif extern int RendererMain(const content::MainFunctionParams&); extern int UtilityMain(const MainFunctionParams&); +#if defined(OS_ANDROID) +extern int DownloadMain(const MainFunctionParams&); +#endif } // namespace content namespace content { +namespace { + +// This sets up two singletons responsible for managing field trials. The +// |field_trial_list| singleton lives on the stack and must outlive the Run() +// method of the process. +void InitializeFieldTrialAndFeatureList( + scoped_ptr* field_trial_list) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + // Initialize statistical testing infrastructure. We set the entropy + // provider to nullptr to disallow non-browser processes from creating + // their own one-time randomized trials; they should be created in the + // browser process. + field_trial_list->reset(new base::FieldTrialList(nullptr)); + + // Ensure any field trials in browser are reflected into the child + // process. + if (command_line.HasSwitch(switches::kForceFieldTrials)) { + bool result = base::FieldTrialList::CreateTrialsFromString( + command_line.GetSwitchValueASCII(switches::kForceFieldTrials), + std::set()); + DCHECK(result); + } + + scoped_ptr feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine( + command_line.GetSwitchValueASCII(switches::kEnableFeatures), + command_line.GetSwitchValueASCII(switches::kDisableFeatures)); + base::FeatureList::SetInstance(std::move(feature_list)); +} + +} // namespace + #if !defined(CHROME_MULTIPLE_DLL_CHILD) base::LazyInstance g_empty_content_browser_client = LAZY_INSTANCE_INITIALIZER; #endif // !CHROME_MULTIPLE_DLL_CHILD -#if !defined(OS_IOS) && !defined(CHROME_MULTIPLE_DLL_BROWSER) -base::LazyInstance - g_empty_content_plugin_client = LAZY_INSTANCE_INITIALIZER; +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) +base::LazyInstance + g_empty_content_gpu_client = LAZY_INSTANCE_INITIALIZER; base::LazyInstance g_empty_content_renderer_client = LAZY_INSTANCE_INITIALIZER; base::LazyInstance g_empty_content_utility_client = LAZY_INSTANCE_INITIALIZER; -#endif // !OS_IOS && !CHROME_MULTIPLE_DLL_BROWSER - -#if defined(OS_WIN) +#endif // !CHROME_MULTIPLE_DLL_BROWSER -#endif // defined(OS_WIN) +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) && defined(OS_ANDROID) +#if defined __LP64__ +#define kV8NativesDataDescriptor kV8NativesDataDescriptor64 +#define kV8SnapshotDataDescriptor kV8SnapshotDataDescriptor64 +#else +#define kV8NativesDataDescriptor kV8NativesDataDescriptor32 +#define kV8SnapshotDataDescriptor kV8SnapshotDataDescriptor32 +#endif +#endif -#if defined(OS_POSIX) && !defined(OS_IOS) +#if defined(OS_POSIX) // Setup signal-handling state: resanitize most signals, ignore SIGPIPE. void SetupSignalHandlers() { @@ -178,7 +211,7 @@ void SetupSignalHandlers() { CHECK_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN)); } -#endif // OS_POSIX && !OS_IOS +#endif // OS_POSIX void CommonSubprocessInit(const std::string& process_type) { #if defined(OS_WIN) @@ -204,7 +237,10 @@ void CommonSubprocessInit(const std::string& process_type) { #if !defined(OFFICIAL_BUILD) // Print stack traces to stderr when crashes occur. This opens up security // holes so it should never be enabled for official builds. - base::debug::EnableInProcessStackDumping(); + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableInProcessStackTraces)) { + base::debug::EnableInProcessStackDumping(); + } #if defined(OS_WIN) base::RouteStdioToConsole(false); LoadLibraryA("dbghelp.dll"); @@ -226,17 +262,19 @@ class ContentClientInitializer { } #endif // !CHROME_MULTIPLE_DLL_CHILD -#if !defined(OS_IOS) && !defined(CHROME_MULTIPLE_DLL_BROWSER) - if (process_type == switches::kPluginProcess || - process_type == switches::kPpapiPluginProcess) { +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + if (process_type == switches::kGpuProcess || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { if (delegate) - content_client->plugin_ = delegate->CreateContentPluginClient(); - if (!content_client->plugin_) - content_client->plugin_ = &g_empty_content_plugin_client.Get(); - // Single process not supported in split dll mode. - } else if (process_type == switches::kRendererProcess || - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kSingleProcess)) { + content_client->gpu_ = delegate->CreateContentGpuClient(); + if (!content_client->gpu_) + content_client->gpu_ = &g_empty_content_gpu_client.Get(); + } + + if (process_type == switches::kRendererProcess || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { if (delegate) content_client->renderer_ = delegate->CreateContentRendererClient(); if (!content_client->renderer_) @@ -252,7 +290,7 @@ class ContentClientInitializer { if (!content_client->utility_) content_client->utility_ = &g_empty_content_utility_client.Get(); } -#endif // !OS_IOS && !CHROME_MULTIPLE_DLL_BROWSER +#endif // !CHROME_MULTIPLE_DLL_BROWSER } }; @@ -301,6 +339,9 @@ int RunZygote(const MainFunctionParams& main_function_params, MainFunctionParams main_params(command_line); main_params.zygote_child = true; + scoped_ptr field_trial_list; + InitializeFieldTrialAndFeatureList(&field_trial_list); + for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { if (process_type == kMainFunctions[i].name) return kMainFunctions[i].function(main_params); @@ -314,7 +355,6 @@ int RunZygote(const MainFunctionParams& main_function_params, } #endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) -#if !defined(OS_IOS) static void RegisterMainThreadFactories() { #if !defined(CHROME_MULTIPLE_DLL_BROWSER) && !defined(CHROME_MULTIPLE_DLL_CHILD) UtilityProcessHostImpl::RegisterUtilityMainThreadFactory( @@ -349,15 +389,15 @@ int RunNamedProcessTypeMain( #endif #if !defined(CHROME_MULTIPLE_DLL_BROWSER) #if defined(ENABLE_PLUGINS) -#if !defined(OS_LINUX) - { switches::kPluginProcess, PluginMain }, -#endif { switches::kPpapiPluginProcess, PpapiPluginMain }, { switches::kPpapiBrokerProcess, PpapiBrokerMain }, #endif // ENABLE_PLUGINS { switches::kUtilityProcess, UtilityMain }, { switches::kRendererProcess, RendererMain }, { switches::kGpuProcess, GpuMain }, +#if defined(OS_ANDROID) + { switches::kDownloadProcess, DownloadMain}, +#endif #endif // !CHROME_MULTIPLE_DLL_BROWSER }; @@ -397,7 +437,6 @@ int RunNamedProcessTypeMain( NOTREACHED() << "Unknown process type: " << process_type; return 1; } -#endif // !OS_IOS class ContentMainRunnerImpl : public ContentMainRunner { public: @@ -417,16 +456,6 @@ class ContentMainRunnerImpl : public ContentMainRunner { Shutdown(); } -#if defined(USE_TCMALLOC) - static bool GetNumericPropertyThunk(const char* name, size_t* value) { - return MallocExtension::instance()->GetNumericProperty(name, value); - } - - static void ReleaseFreeMemoryThunk() { - MallocExtension::instance()->ReleaseFreeMemory(); - } -#endif - int Initialize(const ContentMainParams& params) override { ui_task_ = params.ui_task; @@ -445,35 +474,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { TRACE_EVENT0("startup,benchmark", "ContentMainRunnerImpl::Initialize"); #endif // OS_ANDROID - // NOTE(willchan): One might ask why these TCMalloc-related calls are done - // here rather than in process_util_linux.cc with the definition of - // EnableTerminationOnOutOfMemory(). That's because base shouldn't have a - // dependency on TCMalloc. Really, we ought to have our allocator shim code - // implement this EnableTerminationOnOutOfMemory() function. Whateverz. - // This works for now. -#if !defined(OS_MACOSX) && defined(USE_TCMALLOC) - // For tcmalloc, we need to tell it to behave like new. - tc_set_new_mode(1); - - // On windows, we've already set these thunks up in _heap_init() - base::allocator::SetGetNumericPropertyFunction(GetNumericPropertyThunk); - base::allocator::SetReleaseFreeMemoryFunction(ReleaseFreeMemoryThunk); - - // Provide optional hook for monitoring allocation quantities on a - // per-thread basis. Only set the hook if the environment indicates this - // needs to be enabled. - const char* profiling = getenv(tracked_objects::kAlternateProfilerTime); - if (profiling && - (atoi(profiling) == tracked_objects::TIME_SOURCE_TYPE_TCMALLOC)) { - tracked_objects::SetAlternateTimeSource( - MallocExtension::GetBytesAllocatedOnCurrentThread, - tracked_objects::TIME_SOURCE_TYPE_TCMALLOC); - } -#endif // !OS_MACOSX && USE_TCMALLOC - -#if !defined(OS_IOS) base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance(); -#endif // On Android, // - setlocale() is not supported. @@ -481,7 +482,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { // stack trace when crashing. // - The ipc_fd is passed through the Java service. // Thus, these are all disabled. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if !defined(OS_ANDROID) // Set C library locale to make sure CommandLine can parse argument values // in correct encoding. setlocale(LC_ALL, ""); @@ -489,7 +490,9 @@ class ContentMainRunnerImpl : public ContentMainRunner { SetupSignalHandlers(); g_fds->Set(kPrimaryIPCChannel, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor); -#endif // !OS_ANDROID && !OS_IOS + g_fds->Set(kMojoIPCChannel, + kMojoIPCChannel + base::GlobalDescriptors::kBaseDescriptor); +#endif // !OS_ANDROID #if defined(OS_LINUX) || defined(OS_OPENBSD) g_fds->Set(kCrashDumpSignal, @@ -504,22 +507,18 @@ class ContentMainRunnerImpl : public ContentMainRunner { // The exit manager is in charge of calling the dtors of singleton objects. // On Android, AtExitManager is set up when library is loaded. - // On iOS, it's set up in main(), which can't call directly through to here. // A consequence of this is that you can't use the ctor/dtor-based // TRACE_EVENT methods on Linux or iOS builds till after we set this up. -#if !defined(OS_ANDROID) && !defined(OS_IOS) +#if !defined(OS_ANDROID) if (!ui_task_) { // When running browser tests, don't create a second AtExitManager as that // interfers with shutdown when objects created before ContentMain is // called are destructed when it returns. exit_manager_.reset(new base::AtExitManager); } -#endif // !OS_ANDROID && !OS_IOS +#endif // !OS_ANDROID - // Don't create this loop on iOS, since the outer loop is already handled - // and a loop that's destroyed in shutdown interleaves badly with the event - // loop pool on iOS. -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) // We need this pool for all the objects created before we get to the // event loop, but we don't want to leave them hanging around until the // app quits. Each "main" needs to flush this pool right before it goes into @@ -551,9 +550,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { if (delegate_ && delegate_->ShouldEnableProfilerRecording()) tracked_objects::ScopedTracker::Enable(); -#if !defined(OS_IOS) SetProcessTitleFromCommandLine(argv); -#endif #endif // !OS_ANDROID int exit_code = 0; @@ -567,10 +564,8 @@ class ContentMainRunnerImpl : public ContentMainRunner { std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); -#if !defined(OS_IOS) // Initialize mojo here so that services can be registered. InitializeMojo(); -#endif #if defined(OS_WIN) if (command_line.HasSwitch(switches::kDeviceScaleFactor)) { @@ -636,7 +631,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { TRACE_EVENT0("startup,benchmark", "ContentMainRunnerImpl::Initialize"); #endif // !OS_ANDROID -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) // We need to allocate the IO Ports before the Sandbox is initialized or // the first instance of PowerMonitor is created. // It's important not to allocate the ports for processes which don't @@ -749,7 +744,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { #if defined(OS_WIN) CHECK(InitializeSandbox(params.sandbox_info)); -#elif defined(OS_MACOSX) && !defined(OS_IOS) +#elif defined(OS_MACOSX) if (process_type == switches::kRendererProcess || process_type == switches::kPpapiPluginProcess || (delegate_ && delegate_->DelaySandboxInitialization(process_type))) { @@ -775,6 +770,14 @@ class ContentMainRunnerImpl : public ContentMainRunner { std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); + // Run this logic on all child processes. Zygotes will run this at a later + // point in time when the command line has been updated. + scoped_ptr field_trial_list; + if (!process_type.empty() && process_type != switches::kZygoteProcess) + InitializeFieldTrialAndFeatureList(&field_trial_list); + + base::HistogramBase::EnableActivityReportHistogram(process_type); + MainFunctionParams main_params(command_line); main_params.ui_task = ui_task_; #if defined(OS_WIN) @@ -783,11 +786,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { main_params.autorelease_pool = autorelease_pool_.get(); #endif -#if !defined(OS_IOS) return RunNamedProcessTypeMain(process_type, main_params, delegate_); -#else - return 1; -#endif } void Shutdown() override { @@ -809,7 +808,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { #endif // _CRTDBG_MAP_ALLOC #endif // OS_WIN -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) autorelease_pool_.reset(NULL); #endif diff --git a/chromium/content/app/mojo/mojo_init.cc b/chromium/content/app/mojo/mojo_init.cc index b289aa738b8..828887dead7 100644 --- a/chromium/content/app/mojo/mojo_init.cc +++ b/chromium/content/app/mojo/mojo_init.cc @@ -9,11 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "content/public/common/content_switches.h" #include "ipc/ipc_channel.h" -#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" - -#if defined(MOJO_SHELL_CLIENT) -#include "content/common/mojo/mojo_shell_connection_impl.h" -#endif +#include "mojo/edk/embedder/embedder.h" namespace content { @@ -22,30 +18,8 @@ namespace { class MojoInitializer { public: MojoInitializer() { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - if (0 && process_type.empty() && !command_line.HasSwitch("use-old-edk")) { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - "use-new-edk"); - } - - if (command_line.HasSwitch("use-new-edk")) { - bool initialize_as_parent = process_type.empty(); -#if defined(MOJO_SHELL_CLIENT) - if (IsRunningInMojoShell()) - initialize_as_parent = false; -#endif - if (initialize_as_parent) { - mojo::embedder::PreInitializeParentProcess(); - } else { - mojo::embedder::PreInitializeChildProcess(); - } - } - - mojo::embedder::SetMaxMessageSize(IPC::Channel::kMaximumMessageSize); - mojo::embedder::Init(); + mojo::edk::SetMaxMessageSize(IPC::Channel::kMaximumMessageSize); + mojo::edk::Init(); } }; diff --git a/chromium/content/app/strings/content_strings.grd b/chromium/content/app/strings/content_strings.grd index e56c6856c15..9219357a5b9 100644 --- a/chromium/content/app/strings/content_strings.grd +++ b/chromium/content/app/strings/content_strings.grd @@ -39,7 +39,8 @@ below: @@ -167,6 +168,9 @@ below: Details + + Save + This is a searchable index. Enter search keywords: ''' @@ -266,9 +270,6 @@ below: $1Week 38, 2014, starting on $2September 15, 2014 - - address - article @@ -281,7 +282,10 @@ below: checkbox - + + content info + + definition @@ -296,17 +300,17 @@ below: form + + footer + - - footer - stepper - - toggle button - + + toggle button + heading @@ -346,7 +350,167 @@ below: HTML content - + + + alert + + + alert_dialog + + + application + + + blockquote + + + busy indicator + + + button + + + drop down button + + + cell + + + color picker + + + column header + + + combo box + + + date picker + + + date and time picker + + + dialog + + + directory + + + disclosure triangle + + + document + + + object + + + graphic + + + heading $11 + + + time picker + + + list box + + + log + + + marquee + + + menu + + + menu bar + + + menu button + + + menu item + + + meter + + + note + + + outline + + + pop up button + + + progress indicator + + + radio button + + + radio group + + + row header + + + ruler + + + scroll bar + + + search + + + slider + + + spin button + + + splitter + + + tab + + + table + + + tab list + + + tab panel + + + time + + + timer + + + toolbar + + + tooltip + + + tree + + + tree grid + + + tree item + + press @@ -365,6 +529,12 @@ below: jump + + open + + + click + AM/PM @@ -445,11 +615,11 @@ below: hide closed captions - + play on remote device - + control remote playback @@ -521,7 +691,7 @@ below: play on remote device - + control remote playback diff --git a/chromium/content/app/strings/translations/content_strings_am.xtb b/chromium/content/app/strings/translations/content_strings_am.xtb index e21a9f31462..50d23f929e2 100644 --- a/chromium/content/app/strings/translations/content_strings_am.xtb +++ b/chromium/content/app/strings/translations/content_strings_am.xtb @@ -2,22 +2,30 @@ ባዶ +የምናሌ ንጥል የድምጽ ትራክ ላይ ድምጸ-ከል አድርግ የማቀያየሪያ አዝራር ሰኮንዶች እባክዎ የኢሜይል አድራሻ ያስገቡ። ማሟያ +መልሶ መልስ +ተንሸራታች የቅርብ ጊዜ ፍለጋዎችን አጽዳ +መገናኛ ጽሑፍ ሰንደቅ ክልል በዚህ ወር ፋይሎችን ይምረጡ +የስራ ላይ አመልካች እባክዎ ባዶ ያልሆነ የኢሜይል አድራሻ ያስገቡ። ሌላ... +የማንቂያ_መገናኛ +የዛፍ ፍርግርግ ጥዋት/ከሰዓት በርቀት መሳሪያ ላይ አጫውት ምረጥ +ቢጋር ቀቀ ሁኔታ ዋጋ ወይም ከዚያ በፊት መሆን አለበት። @@ -26,8 +34,10 @@ እባክዎ ይህ ጽሑፍ ወደ ወይም ከዚያ በታች ቁምፊዎች ያሳጥሩት (በአሁኑ ጊዜ ቁምፊዎችን እየተጠቀሙ ነዎት)። ሳምንት አመልካች ሳጥን +የቀን እና የሰዓት መራጭ ቀን እባክዎ ይህን መስክ ይሙሉት። +የትር ዝርዝር የቅርብ ጊዜ ፍለጋዎች የሉም አጫውት እባክዎ የሚሰራ ዋጋ ያስገቡ። የሚቀርበው ዋጋ ነው። @@ -35,16 +45,25 @@ ግርጌ ድምጽ ዋጋ ወይም ከዚያ በኋላ መሆን አለበት። +የብቅ-ባይ አዝራር ለመቀጠል ከፈለጉ እባክዎ ይህ ሳጥን ላይ ምልክት ያድርጉ። 1024(መካከለኛ ደረጃ) +የሬዲዮ ስብስብ የአሁኑ የፊልም ሁኔታ እባክዎ በኮማ የተለዩ የኢሜይል አድራሻዎች ዝርዝር ያስገቡ። +ጠቅ ያድርጉ +ነገር የደመቀ ይዘት አገናኝ እባክዎ የሚሰራ ዋጋ ያስገቡ። መስኩ ያልተጠናቀቀ ነው ወይም ልክ ያልሆነ ቀን አለው። +ሠንጠረዥ +note እባክዎ አንድ ዩአርኤል ያስገቡ። +ሰዓት ቆጣሪ +ጥምድ ሳጥን ዋጋ ከ የሚያንስ ወይም ከእሱ እኩል መሆን አለበት። ብየና +አርዕስት የፊልሙ ቀሪ ሰከንዶች ቀይር ሚሜ @@ -52,60 +71,89 @@ የጊዜ ቆይታ እባክዎ ይህን ጽሑፍ ወደ ቁምፊዎች ወይም ከዚያ በላይ ያራዝሙት (አሁን እየተጠቀሙ ያሉት ቁምፊዎችን ነው)። ከ«» በፊት የሚመጣ ክፍል የ«» ምልክት መያዝ የለበትም። +መተግበሪያ +የማሸብለያ አሞሌ ሚሊሰኮንዶች +ግራፊክ ቀዳሚውን ወር አሳይ ዝለል +color picker +የምናሌ አሞሌ +የይዘት መረጃ እባክዎ የሚሰራ ዋጋ ያስገቡ። ሁለቱ የሚቀርቡ ዋጋዎች እና ናቸው። +የምናሌ አዝራር ዝርዝሮች ቅጽ +ዛፍ ከ«» በኋላ የሚመጣ ክፍል የ«» ምልክት መያዝ የለበትም። +የትር ፓነል ቪዲዮ +ፈልግ እባክዎ ቁጥር ያስገቡ። ተመርጠዋል እባክዎ ከ«» በኋላ አንድ ክፍል ያስገቡ። «» ያልተሟላ ነው። +የሂደት አመልካች ዝርዝር አመልካች ቀጣዩን ወር አሳይ +የረድፍ ራስጌ ዋጋ ከ የሚበልጥ ወይም ከእሱ እኩል መሆን አለበት። እባክዎ አንድ ክፍል ያስገቡና «»ን ያስከትሉ። «» ያልተሟላ ነው። +አስቀምጥ ምስል +የዛፍ ንጥል እባክዎ አንድ ወይም ተጨማሪ ፋይሎችን ይምረጡ። ከሙሉ ማያ ገጽ ውጣ አታመልክት +ቀን መራጭ ድምፅ-ከልን አንሳ +ሰዓት መልሶ ማጫወትን ለአፍታ አቁም +አዝራር ሌላ... ዳግም አስጀምር 2048(ከፍተኛ ደረጃ) ሳምንት -አድራሻ +ማሾሪያ አዝራር ዓመት +የዝርዝር ሳጥን የድምጹ ትራክ ድምጸ-ከል አንሳ አንቃ +ሜትር የፊልም ሰዓት መስመር አውራ ጣት የፊልም ሰዓት አንፏቃቂ አውራ ጣት የወር መምረጫ ፓነል አሳይ የኦዲዮ ሰዓት አንፏቃቂ +ሰዓት መራጭ ወደ ሙሉ ገጽ ዕይታ ግባ +የምዝግብ ማስታወሻ አጽዳ የቅርብ ጊዜ ፍለጋዎችን ሌላ… አስኪያጅ +ማስመሪያ +ጠቃሚ የመሣሪያ ምክር ተሰኪን መጫን አልተቻለም። ድምጸ-ከል ያድርጉ ተጫን የተዘጉ የስዕል መግለጫዎችን ይደብቁ +ማውጫ +የጽሑፍ ጥቅስ የፊልም ሰዓት እባክዎ የተጠየቀውን ቅርጸት ያዛምዱ። ያለፈው ጊዜ እባክዎ በዝርዝሩ ውስጥ አንድ ንጥል ይምረጡ። +የአምድ ራስጌ ፋይል ምረጥ ሌላ... +TAB ሰዓቶች ቀሪ ጊዜ የተዘጉ የስዕል መግለጫዎችን ማሳየት አቁም የኤች ቲ ኤም ኤል ይዘት ላፍታ አቁም +ይፋ ማሳወቂያ ሶስት ማዕዘን እባክዎ በኢሜይል አድራሻው ውስጥ «» ያካትቱ። «» ውስጥ «» ይጎድላል። +መክፈያ ምንም ፋይል አልተመረጠም የጽሑፍ መስክ ፈልግ የተዘጉ የስዕል መግለጫዎችን ማሳየት ጀምር @@ -116,8 +164,11 @@ የምስል ካርታ ልክ ያልሆነ እሴት። አመልክት +የመሣሪያ አሞሌ ፋይሎች እባክዎ ፋይል ይምረጡ። +የሬዲዮ አዝራር +የተቆልቋይ አዝራር ደቂቃዎች የማህደረ መረጃ ቁጥጥር የብየና ዝርዝር @@ -127,10 +178,15 @@ መልሶ ማጫወት ይጀምሩ main ወር +ክፈት የአሁኑ ጊዜ በሰከንዶች አሰሳ የተዘጉ የስዕል መግለጫዎችን አሳይ +ሰነድ ሒሳብ ሌላ... +ማንቂያ ፣ የሚጀምረው በ +ሕዋስ +ምናሌ \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ar.xtb b/chromium/content/app/strings/translations/content_strings_ar.xtb index 067d3a75f11..5fc9afe8780 100644 --- a/chromium/content/app/strings/translations/content_strings_ar.xtb +++ b/chromium/content/app/strings/translations/content_strings_ar.xtb @@ -2,22 +2,30 @@ فارغ +عنصر القائمة كتم صوت المقطع الصوتي زر التبديل ثوانٍ‬‬ الرجاء إدخال عنوان البريد الإلكتروني. تكميلي +صورة بها نص متحرك +شريط تمرير محو آخر عمليات البحث +مربع حوار article إعلان بانر منطقة هذا الشهر اختيار الملفات +مؤشر مشغول يُرجى إدخال عنوان البريد الإلكتروني وعدم ترك الحق فارغًا. آخر... +مربع حوار_للتنبيه +شبكة متفرعة صباحًا/مساءً تشغيل على جهاز بعيد الاختيار +مخطط يوم الحالة يجب أن تكون القيمة أو قبل ذلك. @@ -26,8 +34,10 @@ الرجاء اختصار هذا النص حتى يصل إلى من الحروف أو أقل (أنت الآن تستخدم من الحروف). الأسبوع ، مربع اختيار +منتقي التاريخ والوقت يوم الرجاء ملء هذا الحقل. +قائمة علامات التبويب لا عمليات بحث حديثة تشغيل يُرجى إدخال قيمة صالحة. علمًا بأن أقرب قيمة صالحة هي . @@ -35,16 +45,25 @@ تذييل الصوت يجب أن تكون القيمة أو بعد ذلك. +زر بقائمة منبثقة الرجاء تحديد هذا المربع إذا أردت المتابعة. 1024 (درجة متوسطة) +مجموعة أزرار الاختيار حالة الفيلم الحالية الرجاء إدخال قائمة مفصولة بفواصل لعناوين البريد الإلكتروني. +النقر +كائن المحتوى المميز رابط يرجى إدخال قيمة صالحة. الحقل غير مكتمل أو أن التاريخ غير صالح. +جدول +note ‏الرجاء إدخال عنوان URL. +الموقّت +مربع تحرير وسرد يجب أن تكون القيمة أقل من أو تساوي . تعريف +عنوان عدد الثواني المتبقية من الفيلم تبديل شهر @@ -52,60 +71,89 @@ عبارة يُرجى إطالة هذا النص إلى من الحروف أو أكثر (أنت تستخدم حاليًا من الحروف). الجزء المتبوع بالعلامة "" يجب ألا يشتمل على الرمز "". +التطبيق +شريط التمرير مللي ثانية +الرسم عرض الشهر السابق الدخول +علبة الألوان +شريط قوائم +معلومات المحتوى يُرجى إدخال قيمة صالحة. علمًا بأن القيم الصالحة تتراوح بين و. +زر القائمة التفاصيل نموذج +متفرع يجب ألا يشتمل الجزء الذي يلي العلامة "" على الرمز "". +لوحة علامة التبويب فيديو +بحث الرجاء إدخال عدد. محددة يُرجى إدخال الجزء الذي يلي العلامة ""، حيث إن "" غير مكتمل. +مؤشر التقدم محدّد القائمة عرض الشهر التالي +عنوان الصف يجب أن تكون القيمة أكبر من أو تساوي . يُرجى إدخال جزء متبوع بعلامة ""، حيث إن "" غير مكتمل. +حفظ شكل +عنصر متفرع الرجاء تحديد ملف واحد أو أكثر. إنهاء وضع ملء الشاشة إزالة علامة الاختيار +منتقي التاريخ إعادة الصوت +الوقت إيقاف التشغيل مؤقتًا +زر آخر... إعادة 2048 (درجة عالية) الأسبوع -عنوان +زر الدوران عام +مربع القائمة إعادة صوت المقطع الصوتي تنشيط +متر صورة مصغرة للتسلسل الزمني للفيلم صورة مصغرة لشريط تمرير وقت الفيلم عرض لوحة تحديد الشهر شريط تمرير وقت الصوت +منتقي الوقت تشغيل وضع ملء الشاشة +log محو آخر عمليات البحث ملف تعريف آخر... تخطي +مسطرة +تلميح تعذر تحميل المكوّن الإضافي. كتم الصوت اضغط إخفاء التسميات التوضيحية المغلقة +الدليل +علامة اقتباس فقرة وقت الفيلم الرجاء مطابقة التنسيق المطلوب. المدة المنقضية الرجاء تحديد عنصر من القائمة. +عنوان العمود اختيار ملفّ آخر... +علامة تبويب ساعات المدة المتبقية إيقاف عرض التسميات التوضيحية المغلقة ‏محتوى HTML إيقاف مؤقت +مثلث الإفصاح يُرجى تضمين العلامة "" في عنوان البريد الإلكتروني، حيث يفتقر "" إلى العلامة "". +أداة التقسيم ّلم يتمّ اختيار أيّ ملفّ الحقل النصي للبحث بدء عرض التسميات التوضيحية المغلقة @@ -116,8 +164,11 @@ مخطّط صورة قيمة غير صحيحة الاختيار +شريط الأدوات عدد الملفات: الرجاء تحديد ملف. +زر الاختيار +زر القائمة المنسدلة دقائق التحكم في الوسائط قائمة تعريف @@ -127,10 +178,15 @@ بدء التشغيل main شهر +الفتح المدة الحالية بالثواني تنقل عرض التسميات التوضيحية المغلقة +المستند math آخر... +تنبيه ، ابتداء من +خلية +القائمة \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_bg.xtb b/chromium/content/app/strings/translations/content_strings_bg.xtb index 986322a788f..aea4837571b 100644 --- a/chromium/content/app/strings/translations/content_strings_bg.xtb +++ b/chromium/content/app/strings/translations/content_strings_bg.xtb @@ -2,22 +2,30 @@ празно +елемент от меню заглушаване на аудиозаписа бутон за превключване Секунди Моля, въведете имейл адрес. допълнение +marquee +плъзгач Изчистване на скорошните търсения +диалогов прозорец статия банер регион Този месец Избор на файлове +индикатор за заетост Моля, въведете имейл адрес. Други... +диалогов прозорец със сигнал +дървовидна таблица AM/PM възпроизвеждане на отдалечено устройство Избиране +структура дд състояние Стойността трябва да е или по-рано. @@ -26,8 +34,10 @@ Моля, съкратете този текст до знака или по-малко (понастоящем използвате знака). Седмица , г. квадратче за отметка +инструмент за избор на дата и час Ден Моля, попълнете това поле. +списък с раздели Няма скорошни търсения пускане Моля, въведете валидна стойност. Най-близката такава е . @@ -35,16 +45,25 @@ долен колонтитул звук Стойността трябва да е или по-късно. +бутон с изскачащ прозорец Моля, поставете отметка в това квадратче, ако искате да продължите. 1024 (средна степен на сложност) +група бутони за избор текущо състояние на филма Моля, въведете списък с имейл адреси, разделени със запетаи. +кликвам +обект откроено съдържание връзка Моля, въведете валидна стойност. Данните в полето са непълни или датата е невалидна. +таблица +забележка Моля, въведете URL адрес. +таймер +разгъващ се списък Стойността трябва да е по-малка или равна на . дефиниция +заглавие от ниво брой оставащи секунди от филма превключвател мм @@ -52,60 +71,89 @@ термин Моля, удължете този текст поне до знака (понастоящем използвате ). Текстът преди „“ не бива да съдържа символа „“. +приложение +лента за превъртане Милисекунди +графика Показване на предишния месец преминаване +инструмент за избор на цветове +лента с менюта +информация за съдържанието Моля, въведете валидна стойност. Двете най-близки такива са и . +бутон за меню Подробности формуляр +tree Текстът след „“ не бива да съдържа символа „“. +панел с раздели видео +search Моля, въведете номер. Избрани: Моля, въведете текст след „“. „“ е непълно. +индикатор за напредък списъчен показалец Показване на следващия месец +заглавка на ред Стойността трябва да е по-голяма или равна на . Моля, въведете текст преди „“. „“ е непълно. +Запазване фигура +елемент от дърво Моля, изберете един или повече файлове. изход от цял екран премахване на отметката +инструмент за избор на дата пускане на звука +time поставяне на възпроизвеждането на пауза +бутон Други... Нулиране 2048 (висока степен на сложност) Седмица -адрес +брояч Година +списъчно поле пускане на аудиозаписа активиране +индикатор миниизображение за времевата контрола за филма миниизображение за времевия плъзгач за филма Показване на панела за избиране на месец времеви плъзгач за аудиозаписа +инструмент за избор на час вход за цял екран +регистрационен файл Изчистване Скорошни търсения Друг... stepper +линия +подсказка Приставката не можа да се зареди. заглушаване натискане скриване на надписите +директория +блоков цитат време от филма Моля, спазвайте изисквания формат. изминало време Моля, изберете елемент в списъка. +заглавка на колона Избор на файл Други... +раздел Часове оставащо време спиране на показването на надписите HTML съдържание поставяне на пауза +триъгълник за разкриване на съдържание Моля, включете „“ в имейл адреса. В/ъв „“ липсва „“. +разделител Няма избран файл текстово поле за търсене започване на показването на надписите @@ -116,8 +164,11 @@ карта с изображения Невалидна стойност. отмятане +лента с инструменти файла Моля, изберете файл. +бутон за избор +бутон за падащо меню Минути медийна контрола списък с дефиниции @@ -127,10 +178,15 @@ начало на възпроизвеждането основен елемент Месец +отварям текущо време в секунди навигация показване на надписите +документ математически израз Други... +сигнал – започва от +клетка +меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_bn.xtb b/chromium/content/app/strings/translations/content_strings_bn.xtb index 122e1a7e18b..15921b2a576 100644 --- a/chromium/content/app/strings/translations/content_strings_bn.xtb +++ b/chromium/content/app/strings/translations/content_strings_bn.xtb @@ -2,22 +2,30 @@ খালি +মেনু আইটেম অডিও ট্র্যাক নিঃশব্দ করুন টগল বোতাম সেকেন্ড দয়া করে কোন ইমেল ঠিকানা প্রবেশ করান: পরিপূরক +marquee +স্লাইডার সাম্প্রতিক অনুসন্ধানগুলি সাফ করুন +ডায়ালগ নিবন্ধ ব্যানার অঞ্চল এই মাস ফাইল চয়ন করুন +ব্যস্ততার সূচক দয়া করে একটি খালি না থাকা ইমেল ঠিকানা লিখুন৷ অন্যান্য... +সতর্কতার_ডায়ালগ +ট্রি গ্রিড AM/PM রিমোট ডিভাইসে প্লে করুন নির্বাচন করুন +রূপরেখা dd স্থিতি মানকে অবশ্যই বা আগের হতে হবে৷ @@ -26,8 +34,10 @@ দয়া করে এই পাঠ্যটি টি অক্ষর বা তার কমে (আপনি বর্তমানে টি অক্ষর ব্যবহার করছেন) সংক্ষিপ্ত করুন৷ সপ্তাহ, চেকবাক্স +তারিখ এবং সময় চয়নকারি দিন দয়া করে এই ক্ষেত্রটি পূরণ করুন৷ +ট্যাব তালিকা কোন সাম্প্রতিক অনুসন্ধান নেই চালনা করুন দয়া করে একটি বৈধ মান লিখুন৷ কাছাকাছির বৈধ মান হল @@ -35,16 +45,25 @@ পাদলেখ অডিও মানকে অবশ্যই বা পরবর্তী হতে হবে৷ +পপ আপ বোতাম আপনি যদি এগিয়ে যেতে চান তবে দয়া করে এই বাক্সটি পরীক্ষা করুন৷ 1024 (মধ্যম গ্রেড) +রেডিও বোতাম গোষ্ঠী চলচ্চিত্রের বর্তমান অবস্থা দয়া করে ইমেল ঠিকানাগুলির একটি কমা দিয়ে আলাদা করা মান প্রবেশ করান৷ +ক্লিক করুন +অবজেক্ট হাইলাইট করা সামগ্রী লিঙ্ক দয়া করে একটি বৈধ মান লিখুন৷ ক্ষেত্রটি অসম্পূর্ণ অথবা একটি অবৈধ তারিখ আছে৷ +সারণী +দ্রষ্টব্য দয়া করে একটি বৈধ URL প্রবেশ করুন৷ +টাইমার +কম্বো বাক্স মানটি অবশ্যই এর চেয়ে কম বা সমান হবে৷ সংজ্ঞা +শিরোনাম চলচ্চিত্রের অবশিষ্ট সেকেন্ড পরিবর্তন করুন মিমি @@ -52,60 +71,89 @@ পদ দয়া করে এই পাঠ্যকে ন্যূনতম অক্ষরের বা তার বেশি (আপনি বর্তমানে টি অক্ষর ব্যবহার করেছেন) দৈর্ঘের করুন। '' অনুসরণ করে এমন একটি অংশে '' চিহ্ন থাকা উচিত নয়৷ +অ্যাপ্লিকেশান +স্ক্রোল বার মিলিসেকেন্ড +গ্রাফিক পূর্ববর্তী মাস দেখান লাফ দিন +রঙ চয়নকারী +মেনু বার +সামগ্রীর তথ্য দয়া করে একটি বৈধ মান লিখুন৷ দুটি কাছাকাছির বৈধ মান হল এবং +মেনু বোতাম বিশদ বিবরণ ফর্ম +tree '' অনুসরণ করে এমন একটি অংশে '' চিহ্ন থাকা উচিত নয়৷ +ট্যাব প্যানেল ভিডিও +search দয়া করে একটি সংখ্যা লিখুন৷ টি নির্বাচিত দয়া করে '' অনুসরণ করে একটি অংশ লিখুন৷ '' অসম্পূর্ণ৷ +অগ্রগতি সূচক তালিকা নির্দেশক পরবর্তী মাস দেখান +সারি শিরোলেখ মানটি অবশ্যই এর চেয়ে বেশি বা সমান হবে৷ '' অনুসরণ করে একটি অংশ লিখুন৷ '' অসম্পূর্ণ৷ +সংরক্ষণ করুন আকার +ট্রি আইটেম দয়া করে এক বা একাধিক ফাইল নির্বাচন করুন৷ পূর্ণ স্ক্রীন বন্ধ করুন আনচেক +তারিখ চয়নকারি সশব্দ +time প্লেব্যাক বিরতি +বোতাম অন্যান্য... রিসেট করুন 2048 (উচ্চ গ্রেড) সপ্তাহ -ঠিকানা +ঘোড়ানোর বোতাম বছর +তালিকা বাক্স অডিও ট্র্যাক সশব্দ করুন সক্রিয় করুন +মিটার চলচ্চিত্র টাইমলাইন থাম্ব চলচ্চিত্রের সময় স্ক্রাবার থাম্ব মাস নির্বাচনের প্যানেল দেখান অডিও সময় স্ক্রাবার +সময় চয়নকারি পূর্ণ স্ক্রীনে প্রবেশ করুন +লগ সাফ করুন সাম্প্রতিক অনুসন্ধানগুলি অন্যান্য... পদক্ষেপকারী +রুলার +সরঞ্জামটিপ প্লাগ ইন লোড করা যায়নি। নিঃশব্দ টিপুন বন্ধ করা পরিচয়লিপিগুলি লুকান +ডিরেক্টরি +ব্লক উদ্ধৃতি চলচ্চিত্রের সময় দয়া করে অনুরোধ হওয়া বিন্যাসটি মেলান৷ অতিবাহিত সময় তালিকা থেকে একটি আইটেম নির্বাচন করুন৷ +কলাম শিরোলেখ ফাইল চয়ন করুন অন্যান্য... -ঘন্টা +ট্যাব +ঘণ্টা অবশিষ্ট সময় বদ্ধ পরিচয়লিপিগুলির প্রদর্শন থামান HTML সামগ্রী বিরাম +উম্মোচন ত্রিভুজ ইমেল ঠিনাকাটিতে দয়া করে একটি '' অন্তর্ভুক্ত করুন৷ '' এ একটি '' অনুপস্থিত৷ +স্প্লিটার কোনও ফাইল চয়ন করা হয় নি অনুসন্ধান পাঠ্য ফিল্ড বন্ধ পরিচয়লিপিগুলির প্রদর্শন শুরু করুন @@ -116,8 +164,11 @@ ছবি মানচিত্র অকার্যকর মান৷ চেক করুন +সরঞ্জাম দণ্ড টি ফাইল দয়া করে একটি ফাইল নির্বাচন করুন৷ +রেডিও বোতাম +ড্রপ ডাউন বোতাম মিনিট মিডিয়া নিয়ন্ত্রণ সংজ্ঞার তালিকা @@ -127,10 +178,15 @@ প্লেব্যাক শুরু করুন প্রধান মাস +খুলুন সেকেন্ডে বর্তমান সময় নেভিগেশন বন্ধ করা পরিচয়লিপিগুলি দেখান +দস্তাবেজ গণিত অন্যান্য... +সতর্কতা , এ শুরু +কক্ষ +মেনু \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ca.xtb b/chromium/content/app/strings/translations/content_strings_ca.xtb index c2d8803d0ae..151fd19cf01 100644 --- a/chromium/content/app/strings/translations/content_strings_ca.xtb +++ b/chromium/content/app/strings/translations/content_strings_ca.xtb @@ -2,22 +2,30 @@ buit +element del menú silencia la pista d'àudio botó de commutació Segons Introduïu una adreça electrònica. complementari +marquee +control lliscant Esborra les cerques recents +quadre de diàleg article bàner regió Aquest mes Trieu els fitxers +indicador d'ocupat Introduïu una adreça electrònica. Altres... +alert_dialog +quadrícula d'arbre a. m./p. m. reprodueix al dispositiu remot selecciona +esquema dd estat El valor ha de ser o anterior. @@ -26,8 +34,10 @@ Escurceu aquest text a un màxim de caràcters (ara n'esteu utilitzant ). Setmana , casella de selecció +selector de data i hora Dia Empleneu aquest camp. +llista de pestanyes No hi ha cerques recents reprodueix Introduïu un valor vàlid. El valor vàlid més proper és . @@ -35,16 +45,25 @@ peu àudio El valor ha de ser o posterior. +botó emergent Marqueu aquesta casella si voleu continuar. 1024 (Mitjà) +grup de botons d'opció estat actual de la pel·lícula Introduïu una llista d'adreces electròniques separades per comes. +fer clic +objecte contingut realçat enllaç Introduïu un valor vàlid. El camp està incomplet o conté una data no vàlida. +taula +nota Introduïu un URL. +temporitzador +quadre combinat El valor ha de ser més petit o igual que . definició +capçalera nombre de segons restants de la pel·lícula commutador mm @@ -52,60 +71,89 @@ terme Allargueu aquest text fins a un mínim de caràcters (ara n'utilitzeu ). Un nom d'usuari seguit de "" no pot contenir el símbol "". +aplicació +barra de desplaçament Mil·lisegons +gràfic Mostra el mes anterior salta +selector de color +barra de menús +informació sobre el contingut Introduïu un valor vàlid. Els valors vàlids més propers són i . +botó de menú Detalls formulari +arbre Un domini precedit per "" no pot contenir el símbol "". +tauler de pestanyes vídeo +cerca Introduïu un número. Elements seleccionats: Introduïu un domini precedit per "". "" no és una adreça electrònica completa. +indicador de progrés marcador de llistes Mostra el mes següent +capçalera de la fila El valor ha de ser més gran o igual que . Introduïu un nom d'usuari seguit de "". "" no és una adreça completa. +Desa figura +element de l'arbre Seleccioneu un o diversos fitxers. surt de la pantalla completa desmarca +selector de data activa el so +time pausa la reproducció +botó Altres... Restableix 2048 (Gran) Setmana -adreça +botó de selecció de valors Any +quadre de llista activa el so de la pista d'àudio activa +comptador miniatura de cronologia de pel·lícula miniatura de la barra de moment de la pel·lícula Mostra el tauler de la selecció de mes barra de moment de l'àudio +selector d'hora visualitza en pantalla completa +registre Esborra Cerques recents Altres... desplaçador +regle +descripció emergent El connector no s'ha pogut carregar. silencia prem amaga els subtítols ocults +directori +cita en bloc moment de la pel·lícula Feu servir el format sol·licitat. temps transcorregut Seleccioneu un element de la llista. +capçalera de columna Tria un fitxer Altres... +pestanya Hores temps restant deixa de mostrar subtítols ocults Contingut HTML pausa +triangle desplegable Incloeu el símbol "" a l'adreça electrònica. Al camp "" falta el símbol "". +divisor No s'ha triat cap fitxer camp de text de la cerca comença a mostrar subtítols ocults @@ -116,8 +164,11 @@ mapa d'imatges Valor no vàlid. marca +barra d'eines fitxers Seleccioneu un fitxer. +botó d'opció +botó desplegable Minuts control de mitjans llista de definicions @@ -127,10 +178,15 @@ inicia la reproducció principal Mes +obrir temps actual en segons navegació mostra els subtítols ocults +document matemàtiques Altres... +alerta , a partir del dia +cel·la +menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_cs.xtb b/chromium/content/app/strings/translations/content_strings_cs.xtb index 499ec8496ad..4ad833de376 100644 --- a/chromium/content/app/strings/translations/content_strings_cs.xtb +++ b/chromium/content/app/strings/translations/content_strings_cs.xtb @@ -2,22 +2,30 @@ prázdné +položka nabídky ztlumit zvukovou stopu přepínací tlačítko Sekundy Zadejte prosím e-mailovou adresu. doplňkové +běžící text +posuvník Smazat nedávná vyhledávání +dialogové okno čl banner oblast Tento měsíc Zvolit soubory +indikátor stavu zaneprázdnění Vyplňte e-mailovou adresu. Jiné… +dialog upozornění +stromová mřížka AM/PM přehrát ve vzdáleném zařízení zvolit +osnova dd stav Datum musí být nebo dříve. @@ -26,8 +34,10 @@ Zkraťte prosím tento text na znaků nebo méně. (Aktuálně má znaků.) . týden, zaškrtávací políčko +výběr data a času Den Vyplňte prosím toto pole. +seznam karet Žádná nedávná vyhledávání přehrát Zadejte platnou hodnotu. Nejbližší platná hodnota je . @@ -35,16 +45,25 @@ zápatí zvuk Datum musí být nebo později. +vyskakovací tlačítko Chcete-li pokračovat, zaškrtněte toto políčko. 1024 (Střední kvalita) +skupina přepínačů aktuální stav filmu Zadejte seznam e-mailových adres oddělených čárkami. +kliknout +objekt zvýrazněný obsah odkaz Zadejte prosím platnou hodnotu. Pole obsahuje neúplnou hodnotu nebo neplatné datum. +tabulka +pozn Zadejte prosím adresu URL. +časovač +rozbalovací seznam Hodnota musí být menší nebo rovna . definice +nadpis zbývající čas filmu v sekundách přepínač mm @@ -52,60 +71,89 @@ výraz Prodlužte prosím tento text na či více znaků. (Aktuálně má znaků.) Část před znakem nesmí obsahovat znak . +aplikace +posuvník Milisekundy +obrázek Zobrazit předchozí měsíc přejít +výběr barev +panel nabídky +informace o obsahu Zadejte platnou hodnotu. Dvě nejbližší hodnoty jsou a . +tlačítko nabídky Podrobnosti formulář +strom Část za znakem nesmí obsahovat znak . +panel karty video +search Zadejte prosím číslo. Vybráno: Zadejte část za znakem . Adresa není úplná. +indikátor průběhu značka seznamu Zobrazit další měsíc +záhlaví řádku Hodnota musí být větší nebo rovna . Zadejte část před znakem . Adresa není úplná. +Uložit číslice +položka stromu Vyberte prosím jeden nebo více souborů. ukončit režim celé obrazovky odstranit zaškrtnutí +výběr data zapnout zvuk +čas pozastavit přehrávání +tlačítko Jiné… Resetovat 2048 (vysoká kvalita) Týden -adresa +číselník Rok +seznam zapnout zvukovou stopu aktivovat +měřič náhled na časové ose filmu náhled na posuvníku filmu Zobrazit panel pro výběr měsíců posuvník času zvuku +výběr času přejít do režimu celé obrazovky +protokol Vymazat Nedávná vyhledávání Další... stepper +pravítko +popisek Plugin se nepodařilo načíst. ztlumit zmáčknout skrýt titulky +adresář +bloková citace čas filmu Zadejte hodnotu, která odpovídá požadovanému formátu. přehraný čas Vyberte prosím v seznamu některou položku. +záhlaví sloupce Vybrat soubor Jiné… +karta Hodiny zbývající čas ukončit zobrazování titulků Obsah ve formátu HTML pozastavit +tlačítko k zobrazení skrytého obsahu Do e-mailové adresy zahrňte znak . V adrese chybí znak . +rozdělovač Soubor nevybrán pole pro vyhledání textu zahájit zobrazování titulků @@ -116,8 +164,11 @@ obrázková mapa Neplatná hodnota. zaškrtnout +lišta Počet souborů: Vyberte prosím soubor. +přepínač +tlačítko rozbalovací nabídky Minuty ovládání médií seznam definic @@ -127,10 +178,15 @@ zahájit přehrávání hlavní Měsíc +otevřít aktuální čas v sekundách navigace zobrazit titulky +dokument matematika Jiné… +upozornění , začíná +buňka +nabídka \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_da.xtb b/chromium/content/app/strings/translations/content_strings_da.xtb index 42323406269..b6022aa94f4 100644 --- a/chromium/content/app/strings/translations/content_strings_da.xtb +++ b/chromium/content/app/strings/translations/content_strings_da.xtb @@ -2,22 +2,30 @@ tom +menupunkt slå lydspor fra til/fra-knap Sekunder Angiv en e-mailadresse. supplerende +marquee +skyder Slet nylige søgninger +dialogboks artikel banner område Denne måned Vælg filer +optagetindikator Angiv en e-mailadresse, der ikke er tom. Andet... -AM/PM +alert_dialog +trægitter +f.m./e.m. afspil på en enhed via fjernadgang vælg +kontur dd status Værdien må ikke være senere end . @@ -26,8 +34,10 @@ Forkort denne tekst til tegn eller færre (du bruger i øjeblikket tegn). Uge , afkrydsningsfelt +dato- og tidsvælger Dag Udfyld dette felt. +faneliste Ingen nylige søgninger afspil Angiv en gyldig værdi. Den nærmeste gyldige værdi er . @@ -35,16 +45,25 @@ sidefod lyd Værdien må ikke være tidligere end . +pop op-knap Markér dette felt, hvis du vil fortsætte. 1024 (Mellemklasse) +gruppe af alternativknapper aktuel filmstatus Angiv en kommasepareret liste over e-mailadresser. +klik +objekt fremhævet indhold link Angiv en gyldig værdi. Feltet er ufuldstændigt eller har en ugyldig dato. +tabel +bmrk Angiv en webadresse. +timer +kombifelt Værdien skal være mindre end eller lig med . definition +overskrift antal sekunder tilbage af filmen kontakt mm @@ -52,60 +71,89 @@ term Forlæng denne tekst til eller flere tegn (du bruger i øjeblikket tegn). Den del, der kommer før "", må ikke indeholde "". +applikation +rullepanel Millisekunder +grafik Vis den foregående måned hop +farvevælger +menulinje +indholdsoplysninger Angiv en gyldig værdi. De to nærmeste gyldige værdier er og . +menuknap Detaljer formular +træ Den del, der kommer efter "", må ikke indeholde symbolet "". +fanepanel video +søg Angiv et nummer. er valgt Angiv den del, der kommer efter "". "" er ufuldstændig. +statusindikator listemarkering Vis næste måned +rækkeoverskrift Værdien skal være større end eller lig med . Angiv den del, der kommer før "". "" er ufuldstændig. +Gem tal +træelement Vælg en eller flere filer. afslut fuld skærm fjern markering +datovælger slå lyd til +tidspunkt pause +knap Andet... Nulstil 2048 (Høj klasse) Uge -adresse +skalafelt År +listefelt slå lydspor til aktiver +måler miniature for tidslinje miniature for filmtidsskyder Vis panel til valg af måned afspilningsbjælke for lyd +tidsvælger åbn fuld skærm +log Ryd Nylige søgninger Andre... stepper +lineal +værktøjstip Pluginnet kunne ikke indlæses. slå lyden fra tryk skjul undertekster +indeks +blockquote filmtid Find et match til det anmodede format. forløbet tid Vælg et punkt på listen. +kolonneoverskrift Vælg fil Andet... +fane Timer resterende tid stop visning af undertekster HTML-indhold pause +Trekant til at vise eller skjule indhold E-mailadressen skal indeholde et "". "" mangler et "". +splitter Der er ikke valgt nogen fil tekstfelt til søgning start visning af undertekster @@ -116,8 +164,11 @@ billedekort Ugyldig værdi. markér +værktøjslinje filer Vælg en fil. +alternativknap +rullemenuknap Minutter mediekontrol liste over definitioner @@ -127,10 +178,15 @@ start afspilning hovd Måned +åbn aktuel tid i sekunder navigation vis undertekster +dokument matematik Andet... +underretning , med start +celle +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_de.xtb b/chromium/content/app/strings/translations/content_strings_de.xtb index 7a8c65bf77f..3559c80a823 100644 --- a/chromium/content/app/strings/translations/content_strings_de.xtb +++ b/chromium/content/app/strings/translations/content_strings_de.xtb @@ -2,22 +2,30 @@ leer +Menüpunkt Ton ausschalten Schaltfläche zum Umschalten Sekunden Geben Sie eine E-Mail-Adresse ein. ergänzend +marquee +Schieberegler Vor Kurzem durchgeführte Suchanfragen löschen +Kleines Fenster article Banner Region Aktueller Monat Dateien auswählen +Anzeige für fehlende Verfügbarkeit Geben Sie eine E-Mail-Adresse ein. Andere... +alert_dialog +Baumraster AM/PM auf Remote-Gerät wiedergeben auswählen +Übersicht tt Status Verwenden Sie oder einen früheren Wert. @@ -26,8 +34,10 @@ Kürzen Sie diesen Text auf max.  Zeichen. Zurzeit verwenden Sie  Zeichen. Woche , Kästchen +Datums- und Uhrzeitauswahl Tag Füllen Sie dieses Feld aus. +Tabliste Keine vor Kurzem durchgeführte Suchanfragen Wiedergeben Geben Sie einen gültigen Wert ein. Der nächstliegende gültige Wert ist . @@ -35,16 +45,25 @@ Fußzeile Audio Verwenden Sie oder einen späteren Wert. +Pop-up-Schaltfläche Aktivieren Sie dieses Kästchen, wenn Sie fortfahren möchten. 1024 (mittlere Stufe) +Optionsfeldgruppe Aktueller Filmstatus Geben Sie eine durch Kommas getrennte Liste der E-Mail-Adressen ein. +Klicken +Objekt markierte Inhalte Link Geben Sie einen gültigen Wert ein. Das Feld ist nicht vollständig oder enthält ein ungültiges Datum. +Tabelle +note Geben Sie eine URL ein. +Timer +Kombinationsfeld Wert muss kleiner als oder gleich sein. Definition +Überschrift  Verbleibende Dauer in Sekunden wechseln mm @@ -52,60 +71,89 @@ Begriff Verlängern Sie diesen Text auf mindestens Zeichen. Derzeit verwenden Sie Zeichen. Vor dem -Zeichen darf das Zeichen "" nicht verwendet werden. +Anwendung +Bildlaufleiste Millisekunden +Grafik Vorherigen Monat anzeigen springen +color picker +Menüleiste +Inhaltsinformationen Geben Sie einen gültigen Wert ein. Die zwei nächstliegenden gültigen Werte sind und . +Menüschaltfläche Details Formular +tree Nach dem -Zeichen darf das Zeichen "" nicht verwendet werden. +Tabsteuerfeld Video +Suchen Geben Sie eine Nummer ein. ausgewählt Geben Sie etwas nach dem -Zeichen ein. Die Angabe "" ist unvollständig. +Fortschrittsanzeige Listenmarkierung Nächsten Monat anzeigen +Zeilenüberschrift Wert muss größer als oder gleich sein. Geben Sie etwas vor dem -Zeichen ein. Die Angabe "" ist unvollständig. +Speichern Zahl +Baumelement Wählen Sie eine oder mehrere Dateien aus. Vollbildmodus beenden Auswahl aufheben +Datumsauswahl Ton an +time Wiedergabe anhalten +Schaltfläche Andere... Zurücksetzen 2048 (High Grade) Woche -Adresse +Drehfeld Jahr +Listenfeld Ton anschalten aktivieren +Messinstrument Zeitachsen-Ziehpunkt Ziehpunkt für Video-Zeitachse Auswahlbereich für Monatsanzeige Audio-Zeitachse +Uhrzeitauswahl Vollbildmodus aktivieren +log Löschen Vor Kurzem durchgeführte Suchanfragen Andere... Stepper +Lineal +Kurzinfo Plug-in konnte nicht geladen werden. Stumm klicken Untertitel ausblenden +Verzeichnis +blockquote Filmdauer Ihre Eingabe muss mit dem geforderten Format übereinstimmen. Vergangene Zeit Wählen Sie ein Element in der Liste aus. +Spaltenüberschrift Datei auswählen Andere... +tab Stunden Verbleibende Zeit Keine Untertitel mehr anzeigen HTML-Inhalte Pausieren +Aufklappdreieck Die E-Mail-Adresse muss ein -Zeichen enthalten. In der Angabe "" fehlt ein -Zeichen. +Teilungsfunktion Keine ausgewählt Feld für den Suchtext Untertitel ab sofort anzeigen @@ -116,8 +164,11 @@ Imagemap Ungültiger Wert. auswählen +Symbolleiste Dateien Wählen Sie eine Datei aus. +Optionsfeld +Drop-down-Schaltfläche Minuten Mediensteuerung Definitionsliste @@ -127,10 +178,15 @@ Wiedergabe starten main Monat +Öffnen Aktuelle Dauer in Sekunden Navigation Untertitel anzeigen +Dokument math Andere... +Benachrichtigung ab dem +Zelle +Menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_el.xtb b/chromium/content/app/strings/translations/content_strings_el.xtb index 730b1041933..bac8a3d4534 100644 --- a/chromium/content/app/strings/translations/content_strings_el.xtb +++ b/chromium/content/app/strings/translations/content_strings_el.xtb @@ -2,22 +2,30 @@ κενό +στοιχείο μενού σίγαση ήχου κουμπί εναλλαγής Δευτερόλεπτα Εισαγάγετε μια διεύθυνση ηλεκτρονικού ταχυδρομείου. συμπληρωματικό +μαρκίζα +ρυθμιστικό Διαγραφή πρόσφατων αναζητήσεων +παράθυρο διαλόγου άρθρο banner περιοχή Αυτόν το μήνα Επιλογή αρχείων +ένδειξη "απασχολημένος" Καταχωρίστε μια διεύθυνση ηλεκτρονικού ταχυδρομείου. Άλλες… +παράθυρο διαλόγου ειδοποιήσεων +πλέγμα δέντρου Π.Μ./Μ.Μ. αναπαραγωγή σε απομακρυσμένη συσκευή επιλογή +διάρθρωση ηη κατάσταση Η τιμή πρέπει να είναι ή προγενέστερη. @@ -26,8 +34,10 @@ Κάντε πιο σύντομο αυτό το κείμενο ώστε να έχει το πολύ χαρακτήρες (αυτήν τη στιγμή χρησιμοποιείτε χαρακτήρες). Εβδομάδα , πλαίσιο ελέγχου +εργαλείο επιλογής ημερομηνίας και ώρας Ημέρα Συμπληρώστε αυτό το πεδίο. +λίστα καρτελών Δεν υπάρχουν πρόσφατες αναζητήσεις αναπαραγωγή Καταχωρίστε μια έγκυρη τιμή. Η κοντινότερη έγκυρη τιμή είναι . @@ -35,16 +45,25 @@ υποσέλιδο ήχος Η τιμή πρέπει να είναι ή μεταγενέστερη. +αναδυόμενο κουμπί Αν θέλετε να συνεχίσετε, επιλέξτε αυτό το πλαίσιο. 1024 (Μέτριος βαθμός) +ομάδα κουμπιών επιλογής τρέχουσα κατάσταση ταινίας Εισαγάγετε μια λίστα διευθύνσεων ηλεκτρονικού ταχυδρομείου διαχωρισμένη με κόμματα. +κάνω κλικ +αντικείμενο επισημασμένο περιεχόμενο σύνδεσμος Καταχωρίστε μια έγκυρη τιμή. Το πεδίο είναι ελλιπές ή περιέχει μη έγκυρη ημερομηνία. +πίνακας +σημείωση Εισαγάγετε μια διεύθυνση URL. +χρονόμετρο +σύνθετο πλαίσιο Η τιμή πρέπει να είναι μικρότερη ή ίση του . ορισμός +επικεφαλίδα αριθμός δευτερολέπτων της ταινίας που απομένουν εναλλαγή μμ @@ -52,60 +71,89 @@ όρος Αυξήστε την έκταση αυτού του κειμένου στους χαρακτήρες ή περισσότερο (αυτήν τη στιγμή χρησιμοποιείτε χαρακτήρες). Το τμήμα της διεύθυνσης πριν το σύμβολο "" δεν πρέπει να περιέχει το σύμβολο "". +εφαρμογή +γραμμή κύλισης Χιλιοστά του δευτερολέπτου +γραφικό Εμφάνιση προηγούμενου μήνα μεταπήδηση +επιλογέας χρώματος +γραμμή μενού +πληροφορίες περιεχομένου Καταχωρίστε μια έγκυρη τιμή. Οι δύο πιο κοντινές έγκυρες τιμές είναι και . +κουμπί μενού Λεπτομέρειες φόρμα +δέντρο Το τμήμα της διεύθυνσης μετά το σύμβολο "" δεν πρέπει να περιέχει το σύμβολο "". +παράθυρο καρτέλας βίντεο +search Εισαγάγετε έναν αριθμό. Επιλέχτηκαν Καταχωρίστε το τμήμα της διεύθυνσης μετά το σύμβολο "". Η διεύθυνση "" δεν είναι πλήρης. +ένδειξη προόδου δείκτης λίστας Εμφάνιση επόμενου μήνα +κεφαλίδα σειράς Η τιμή πρέπει να είναι μεγαλύτερη ή ίση του . Καταχωρίστε το τμήμα της διεύθυνσης πριν το σύμβολο "". Η διεύθυνση "" δεν είναι πλήρης. +Αποθήκευση αριθμός +στοιχείο δέντρου Επιλέξτε ένα ή περισσότερα αρχεία. έξοδος από πλήρη οθόνη απενεργοποίηση +εργαλείο επιλογής ημερομηνίας κατάργηση σίγασης +ώρα παύση αναπαραγωγής +κουμπί Άλλες… Επαναφορά 2048 (Υψηλός βαθμός) Εβδομάδα -διεύθυνση +κουμπί αυξομείωσης Έτος +πλαίσιο λίστας κατάργηση σίγασης ήχου ενεργοποίηση -δείκτης κύλισης χρονικού πλαισίου ταινίας +μετρητής +δείκτης κύλισης χρονολογίου ταινίας δείκτης κύλισης χρόνου ταινίας Εμφάνιση παραθύρου επιλογής μήνα δείκτης χρόνου ήχου +εργαλείο επιλογής ώρας ενεργοποίηση πλήρους οθόνης +αρχείο καταγραφής Διαγραφή Πρόσφατες αναζητήσεις Άλλο... stepper +χάρακας +επεξήγηση εργαλείου Δεν είναι δυνατή η φόρτωση της προσθήκης. σίγαση πατήστε απόκρυψη υπότιτλων +κατάλογος +blockquote χρόνος ταινίας Αντιστοιχίστε τη ζητούμενη μορφή. χρόνος που παρήλθε Επιλέξτε ένα στοιχείο από τη λίστα. +κεφαλίδα στήλης Επιλογή αρχείου Άλλες… +καρτέλα Ώρες χρόνος που απομένει διακοπή προβολής υπότιτλων Περιεχόμενο HTML παύση +τρίγωνο εμφάνισης/απόκρυψης Συμπεριλάβετε το σύμβολο "" στη διεύθυνση ηλεκτρονικού ταχυδρομείου. Από τη διεύθυνση "" λείπει το σύμβολο "". +διαχωριστής Δεν επιλέχθηκε κανένα αρχείο. αναζήτηση πεδίου κειμένου έναρξη προβολής υπότιτλων @@ -116,8 +164,11 @@ χάρτης εικόνας Μη έγκυρη τιμή. ενεργοποίηση +γραμμή εργαλείων αρχεία Επιλέξτε ένα αρχείο. +κουμπί επιλογής +αναπτυσσόμενο κουμπί Λεπτά έλεγχος μέσων λίστα ορισμών @@ -127,10 +178,15 @@ έναρξη αναπαραγωγής κύριο Μήνας +ανοίγω τρέχων χρόνος σε δευτερόλεπτα πλοήγηση εμφάνιση υπότιτλων +έγγραφο μαθηματικά Άλλες… +ειδοποίηση , από τις +κελί +μενού \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_en-GB.xtb b/chromium/content/app/strings/translations/content_strings_en-GB.xtb index b819bc38e70..8e9b336a81d 100644 --- a/chromium/content/app/strings/translations/content_strings_en-GB.xtb +++ b/chromium/content/app/strings/translations/content_strings_en-GB.xtb @@ -2,22 +2,30 @@ blank +menu item mute audio track toggle button Seconds Please enter an email address. complementary +marquee +slider Clear Recent Searches +dialogue article banner region This month Choose Files +busy indicator Please enter a non-empty email address. Other... +alert_dialogue +tree grid AM/PM play on remote device select +outline dd status Value must be or earlier. @@ -26,8 +34,10 @@ Please shorten this text to characters or less (you are currently using characters). Week , Tick box +date and time picker Day Please fill in this field. +tab list No recent searches play Please enter a valid value. The nearest valid value is . @@ -35,16 +45,25 @@ footer audio Value must be or later. +pop-up button Please tick this box if you want to proceed. 1024 (Medium Grade) +radio group current film status Please enter a comma-separated list of email addresses. +click +object highlighted content link Please enter a valid value. The field is incomplete or has an invalid date. +table +note Please enter a URL. +timer +combo box Value must be less than or equal to . definition +heading number of seconds of film remaining switch mm @@ -52,60 +71,89 @@ term Please lengthen this text to characters or more (you are currently using characters). A part followed by '' should not contain the symbol ''. +application +scroll bar Milliseconds +graphic Show previous month jump +colour picker +menu bar +content info Please enter a valid value. The two nearest valid values are and . +menu button Details form +tree A part following '' should not contain the symbol ''. +tab panel video +search Please enter a number. selected Please enter a part following ''. '' is incomplete. +progress indicator list marker Show next month +row header Value must be greater than or equal to . Please enter a part followed by ''. '' is incomplete. +Save figure +tree item Please select one or more files. exit full screen untick +date picker un-mute +time pause playback +button Other... Reset 2048 (High Grade) Week -address +spin button Year +list box un-mute audio track activate +meter film timeline thumb film time scrubber thumb Show month selection panel audio time scrubber +time picker enter full screen +log Clear Recent Searches Other... stepper +ruler +tooltip Couldn't load plug-in. mute press hide closed captions +directory +blockquote film time Please match the format requested. elapsed time Please select an item in the list. +column header Choose file Other... +tab Hours remaining time stop displaying closed captions HTML content pause +disclosure triangle Please include an '' in the email address. '' is missing an ''. +splitter No file chosen search text field start displaying closed captions @@ -116,8 +164,11 @@ image map Invalid value. tick +toolbar files Please select a file. +radio button +drop-down button Minutes media control definition list @@ -127,10 +178,15 @@ begin playback main Month +open current time in seconds navigation show closed captions +document math Other... +alert , starting on +cell +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_es-419.xtb b/chromium/content/app/strings/translations/content_strings_es-419.xtb index be71f95b901..64452f99bf6 100644 --- a/chromium/content/app/strings/translations/content_strings_es-419.xtb +++ b/chromium/content/app/strings/translations/content_strings_es-419.xtb @@ -2,22 +2,30 @@ espacio en blanco +elemento del menú silenciar pista de audio botón de activación Segundos Ingresa una dirección de correo electrónico. complementario +marquee +control deslizante Eliminar búsquedas recientes +diálogo artículo banner región Este mes Elegir archivos +indicador de ocupado Ingresa una dirección de correo electrónico que no esté vacía. Otra... +alert_dialog +cuadrícula de árbol AM/PM jugar en el dispositivo remoto seleccionar +contorno dd estado El valor debe ser igual o anterior a . @@ -26,8 +34,10 @@ Acorta este texto a caracteres o menos (actualmente estás usando caracteres). Semana , casilla de verificación +selector de fecha y hora Día Completa este campo +lista de pestañas No hay búsquedas recientes reproducir Ingresa un valor válido. El valor válido más aproximado es . @@ -35,16 +45,25 @@ pie de página audio El valor debe ser igual o posterior a . +botón con ventana emergente Controla esta casilla si deseas continuar. 1024 (Mediano) +grupo de botones de selección estado actual de la película Ingresa una lista de direcciones de correo electrónico separadas por coma +hacer clic +objeto contenido destacado enlace Debes ingresar un valor válido. El campo está incompleto o contiene una fecha no válida. +tabla +nota Ingresa una URL. +cronómetro +cuadro combinado El valor debe ser menor de o igual a definición +encabezado cantidad de segundos restantes de la película cambiar mm @@ -52,60 +71,89 @@ término Alarga el texto a o más caracteres (actualmente, usas  caracteres). El texto antes del signo "" no debe incluir el símbolo "". +aplicación +barra de desplazamiento Milisegundos +gráfico Mostrar el mes anterior saltar +selector de color +barra de menús +información del contenido Ingresa un valor válido. Los dos valores válidos más aproximados son y . +botón de menú Detalles formulario +tree El texto después del signo "" no debe incluir el símbolo "". +panel de pestañas video +search Debes ingresar un número. elementos seleccionados Ingresa texto después del signo "". La dirección "" está incompleta. +indicador de progreso marcador de listas Mostrar el mes siguiente +encabezado de fila El valor debe ser mayor de o igual a Ingresa texto antes del signo "". La dirección "" está incompleta. +Guardar figura +elemento de árbol Selecciona uno o más archivos. salir de pantalla completa desmarcar +selector de fecha desactivar silencio +time pausar reproducción +botón Otra... Restablecer 2048 (Grado elevado) Semana -dirección +botón de control numérico Año +cuadro de lista desactivar silencio de la pista de audio activar +medidor miniatura de línea de tiempo de la película miniatura del control deslizante de duración de la película Mostrar el panel de selección de meses control deslizante de duración del audio +selector de hora ingresar a pantalla completa +rgstr Borrar Búsquedas recientes Otro... secuenciador +regla +información sobre la herramienta No se pudo cargar el complemento. silencio hacer clic ocultar los subtítulos +directorio +bloque entrecomillado horario de película Haz coincidir el formato solicitado. tiempo transcurrido Selecciona un elemento de la lista +encabezado de columna Seleccionar archivo Otra... +tab Horas tiempo restante dejar de mostrar subtítulos Contenido HTML pausa +triángulo desplegable Incluye un signo "" en la dirección de correo electrónico. La dirección "" no incluye el signo "". +separador No se eligió archivo campo de texto de búsqueda empezar a mostrar subtítulos @@ -116,8 +164,11 @@ mapa de imágenes Valor no válido. marcar +barra de herramientas archivos Selecciona un archivo. +botón de selección +botón desplegable Minutos control de medios lista de definiciones @@ -127,10 +178,15 @@ comenzar la reproducción ppal Mes +abrir tiempo actual en segundos navegación mostrar subtítulos +documento expr mtmtc Otra... +alerta , a partir del +celda +menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_es.xtb b/chromium/content/app/strings/translations/content_strings_es.xtb index 72266f3acd4..68e999fce5c 100644 --- a/chromium/content/app/strings/translations/content_strings_es.xtb +++ b/chromium/content/app/strings/translations/content_strings_es.xtb @@ -2,22 +2,30 @@ vacío +elemento de menú silenciar pista de audio botón de activación Segundos Introduce una dirección de correo electrónico complementario +marquee +control deslizante Eliminar búsquedas recientes +cuadro de diálogo artículo banner región Este mes Elegir archivos +indicador de carga Introduce una dirección de correo electrónico que no esté vacía. Otra... +alert_dialog +cuadrícula de árbol A.M./P.M. reproducir en dispositivo remoto seleccionar +contorno dd estado El valor debe ser igual o anterior a . @@ -26,8 +34,10 @@ Reduce la longitud de este texto a caracteres o menos (actualmente, el texto tiene caracteres) Semana , casilla de verificación +selector de fecha y hora Día Completa este campo +lista de pestañas No hay búsquedas recientes reproducir Introduce un valor válido. El valor válido más aproximado es . @@ -35,16 +45,25 @@ pie de página audio El valor debe ser igual o posterior a . +botón emergente Selecciona esta casilla de verificación si quieres continuar 1024 (Mediano) +grupo de selección estado actual de la película Introduce una lista de direcciones de correo electrónico separada por comas +hacer clic +objeto contenido resaltado enlace Debes introducir un valor válido. El campo está incompleto o incluye una fecha no válida. +tabla +nota Introduce una URL +temporizador +cuadro combinado El valor debe inferior o igual a definición +encabezado número de segundos restantes de la película interruptor mm @@ -52,60 +71,89 @@ término Aumenta la longitud del texto a caracteres como mínimo (actualmente, el texto tiene caracteres). El texto seguido del signo "" no debe incluir el símbolo "". +aplicación +barra de desplazamiento Millisegundos +gráfico Mostrar mes anterior saltar +selector de color +barra de menús +información del contenido Introduce un valor válido. Los dos valores válidos más aproximados son y . +botón de menú Detalles formulario +tree El texto detrás del signo "" no debe incluir el símbolo "". +panel de pestaña vídeo +buscar Debes introducir un número. seleccionados Introduce texto detrás del signo "". La dirección "" está incompleta. +indicador de progreso marcador de listas Mostrar mes siguiente +encabezado de fila El valor debe superior o igual a Introduce texto seguido del signo "". La dirección "" está incompleta. +Guardar cifra +elemento del árbol Selecciona uno o varios archivos salir de pantalla completa desmarcar +selector de fecha activar sonido +time pausar reproducción +botón Otra... Restablecer 2048 (Grado elevado) Semana -dirección +botón de control numérico Año +cuadro de lista activar sonido de la pista de audio activar +medidor miniatura de línea de tiempo de la película miniatura del control deslizante de duración de la película Mostrar panel para seleccionar el mes control deslizante de duración de audio +selector de hora activar pantalla completa +registro Eliminar Búsquedas recientes Otros... secuenciador +regla +descripción emergente No se ha podido cargar el complemento. silenciar pulsar ocultar subtítulos opcionales +directorio +blockquote cronología de la película Utiliza un formato que coincida con el solicitado tiempo transcurrido Selecciona un elemento de la lista +encabezado de columna Seleccionar archivo Otra... +tabulador Horas tiempo restante dejar de mostrar subtítulos opcionales Contenido HTML pausar +triángulo de revelación Incluye un signo "" en la dirección de correo electrónico. La dirección "" no incluye el signo "". +divisor Ningún archivo seleccionado campo para buscar texto iniciar la visualización de subtítulos opcionales @@ -116,8 +164,11 @@ mapa de imágenes Valor no válido marcar +barra de herramientas archivos Selecciona un archivo +botón de selección +botón desplegable Minutos control de medios lista de definiciones @@ -127,10 +178,15 @@ iniciar reproducción principal Mes +abrir tiempo actual en segundos navegación mostrar subtítulos opcionales +documento expresión matemática Otra... +alerta , a partir del +celda +menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_et.xtb b/chromium/content/app/strings/translations/content_strings_et.xtb index 43053ac56a0..40771dbbd46 100644 --- a/chromium/content/app/strings/translations/content_strings_et.xtb +++ b/chromium/content/app/strings/translations/content_strings_et.xtb @@ -2,22 +2,30 @@ tühi +menüü-üksus heliraja vaigistamine ümberlülitusnupp Sekundid Sisestage meiliaadress. täiendav +marquee +liugur Kustuta viimased otsingud +dialoog artikkel bänner piirkond See kuu Vali failid +hõivatuse näidik Sisestage mittetühi e-posti aadress. Muu ... +hoiatusdialoog +puuruudustik AM/PM kaugseadmes esitamine vali +kontuur pp olek Väärtus peab olema või varasem. @@ -26,8 +34,10 @@ Lühendage seda teksti tähemärgini või rohkem (praegu kasutate tähemärki). Nädal , märkeruut +kuupäeva ja kellaaja valija päev Täitke see väli. +vahelehtede loend Pole viimaseid otsingud esitus Sisestage kehtiv väärtus. Lähim kehtiv väärtus on . @@ -35,16 +45,25 @@ jalus heli Väärtus peab olema või hilisem. +hüpikunupp Märkige see ruut, kui soovite jätkata. 1024 (keskmine) +raadionuppude grupp video praegune olek Sisestage meiliaadresside loend komadega eraldatult. +klõpsake +objekt esiletõstetud sisu link Sisestage kehtiv väärtus. Väli on täitmata või sisaldab sobimatut kuupäeva. +tabel +märge Sisestage URL. +taimer +liitkast Väärtus peab olema väiksem või võrdne -ga. definitsioon +päis video järelejäänud aeg sekundites lüliti kk @@ -52,60 +71,89 @@ termin Pikendage teksti vähemalt tähemärgini (kasutate praegu tähemärki). Märgile „” eelnev osa ei tohi sisaldada sümbolit „”. +rakendus +kerimisriba Millisekundid +graafika Eelmise kuu kuvamine liigu +värvivalija +menüüriba +sisu teave Sisestage kehtiv väärtus. Kaks lähimat kehtivat väärtust on ja . +menüünupp Üksikasjad vorm +tree Märgile „” järgnev osa ei tohi sisaldada sümbolit „”. +vahelehepaneel video +otsing Sisestage arv. Valitud on üksust Sisestage märgile „” järgnev osa. Aadress „” pole täielik. +edenemise näidik loendilooja Järgmise kuu kuvamine +rea päis Väärtus peab olema suurem või võrdne -ga. Sisestage märgile „” eelnev osa. Aadress „” pole täielik. +Salvesta joonis +puuüksus Valige üks või mitu faili. täisekraanilt väljumine eemalda mrgistus +kuupäeva valija vaigistuse tühistamine +time taasesituse peatamine +nupp Muu ... Lähtesta 2048 (kõrge) Nädal -aadress +pöördnupp Aasta +loendikast heliraja vaigistamise tühistamine aktiveeri +mõõdik video ajajoone pisipilt video ajakursori pisipilt Kuu valikupaneeli kuvamine heli ajamõõdik +kellaaja valija kuvamine täisekraanil +logi Tühjenda Viimased otsingud Muu... stepper +joonlaud +tööriistavihje Pistikprogrammi ei saanud laadida. vaigista vajuta subtiitrite peitmine +kataloog +plokktsitaat video aeg Vastendage nõutav vorming. möödunud aeg Valige loendist element. +veeru päis Vali fail Muu ... +Tabulaator Tunnid järelejäänud aeg subtiitrite kuvamise peatamine HTML-sisu peata +avalikustamise kolmnurk Lisage e-posti aadressile märk „”. Aadressist „” puudub märk „”. +jagaja Pole valitud otsinguteksti väli subtiitrite kuvamise alustamine @@ -116,8 +164,11 @@ hüperpilt Kehtetu väärtus. mrgista +tööriistariba faili Valige üks fail. +raadionupp +rippmenüü nupp Minutid meedia juhtimine definitsioonide loend @@ -127,10 +178,15 @@ taasesituse alustamine pea kuu +avage praegune aeg sekundites navigeerimine subtiitrite kuvamine +dokument matemaatika Muu ... +hoiatus alates kuupäevast +lahter +menüü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fa.xtb b/chromium/content/app/strings/translations/content_strings_fa.xtb index 22bd4ba5559..bf1cc8ff553 100644 --- a/chromium/content/app/strings/translations/content_strings_fa.xtb +++ b/chromium/content/app/strings/translations/content_strings_fa.xtb @@ -2,22 +2,30 @@ خالی +مورد منو بیصداکردن تراک صوتی دکمه تغییر حالت ثانیه لطفاً یک آدرس ایمیل وارد کنید. تکمیلی +نوشتار متحرک روی صفحه +لغزنده پاک کردن جستجوهای اخیر +کادر گفتگو article برنما منطقه این ماه انتخاب فایل‌ها +نشانگر مشغول لطفاً یک آدرس ایمیل غیرخالی وارد کنید. موارد دیگر... +alert_dialog +شبکه درختی ق.ظ/ب.ظ پخش در دستگاه راه دور انتخاب +طرح کلی dd وضعیت مقدار باید یا قبل از آن باشد. @@ -26,8 +34,10 @@ لطفاً این متن را به اندازه نویسه یا کمتر کوتاه کنید (شما در حال حاضر از نویسه استفاده می‌کنید). هفته ، کادر تأیید +انتخابگر تاریخ و زمان روز لطفاً این قسمت را تکمیل کنید. +فهرست برگه جستجوی جدیدی وجود ندارد پخش لطفاً یک مقدار معتبر وارد کنید. نزدیک‌ترین مقدار معتبر است. @@ -35,16 +45,25 @@ پانویس صدا مقدار باید یا بعد از آن باشد. +دکمه بازشو درصورتی‌که می‌خواهید ادامه دهید، این کادر را انتخاب کنید. 1024 (درجه متوسط) +گروه رادیویی وضعیت کنونی فیلم لطفاً لیستی از آدرس‌های ایمیل که با کاما از هم جدا شده‌اند را وارد کنید. +کلیک کنید +شیء محتوای برجسته شده پیوند لطفاً یک مقدار معتبر وارد کنید. این قسمت کامل نیست و یا تاریخ نامعتبر است. +جدول +نت لطفاً یک نشانی وب وارد کنید. +تایمر +کادر ترکیبی مقدار باید کمتر یا برابر با باشد. معنی +عنوان مقدار ثانیه‌های باقیمانده فیلم تعویض mm @@ -52,60 +71,89 @@ اصطلاح لطفاً این نوشتار را به نویسه یا بیشتر افزایش دهید (درحال حاضر از نویسه استفاده می‌کنید). قسمت قبل از «» نباید حاوی نماد «» باشد. +برنامه +نوار پیمایش میلی‌ ثانیه +گرافیک نمایش ماه قبلی پرش +انتخابگر رنگ +نوار منو +اطلاعات محتوا لطفاً یک مقدار معتبر وارد کنید. نزدیک‌ترین مقادیر معتبر و هستند. +دکمه منو جزئیات فرم +درخت قسمت بعد از «» نباید حاوی نماد «» باشد. +پانل برگه ویدیو +جستجو لطفاً شماره‌ای را وارد کنید. انتخاب شد لطفاً قسمت بعد از «» را وارد کنید. «» ناقص است. -علامت گذار لیست +نشانگر پیشرفت +علامت گذار فهرست نمایش ماه بعدی +عنوان ردیف مقدار باید بیشتر یا مساوی با باشد. لطفاً قسمت قبل از «» را وارد کنید. «» ناقص است. +ذخیره شکل +مورد درختی لطفاً یک یا چند فایل را انتخاب کنید. خروج از حالت تمام صفحه برداشتن علامت +انتخابگر تاریخ صدادارکردن +زمان مکث بازپخش +دکمه موارد دیگر... بازنشانی 2048 (درجه بالا) هفته -آدرس +دکمه چرخش سال +کادر فهرست صدادارکردن تراک صوتی فعالسازی +متر نشانگر خط زمان فیلم نشانگر حذف‌کننده زمان فیلم نمایش پانل انتخاب ماه انتخابگر زمان صدا +انتخابگر زمان رفتن به حالت تمام صفحه +log پاک کردن جستجوهای جدید دیگر... گام به گام +خط‌کش +نکته ابزار افزایه بارگیری نشد. بیصداکردن فشار دادن پنهان کردن توصیف‌های بسته +فهرست راهنما +نقل‌قول زمان فیلم لطفاً با قالب درخواستی مطابقت دهید. مدت سپری شده -لطفاً یک مورد را در لیست انتخاب کنید. +لطفاً یک مورد را در فهرست انتخاب کنید. +عنوان ستون انتخاب فایل موارد دیگر... +برگه ساعت زمان باقی‌مانده توقف نمایش توصیف‌های بسته ‏محتوای HTML مکث +مثلث افشا لطفاً نماد «» را به آدرس ایمیل اضافه کنید. «» در «» موجود نیست. +تقسیم‌کننده فایلی انتخاب نشده است فیلد نوشتاری جستجو شروع به نمایش توصیف‌های بسته @@ -116,8 +164,11 @@ نقشه تصویر مقدار نامعتبر. علامت‌گذاری +نوار ابزار فایل لطفاً یک فایل انتخاب کنید. +دکمه رادیو +دکمه کرکره‌ای دقیقه کنترل رسانه فهرست معنی‌ها @@ -127,10 +178,15 @@ شروع بازپخش اصلی ماه +باز کنید زمان کنونی به ثانیه پیمایش نمایش توصیف‌های بسته +سند حساب موارد دیگر... +هشدار ، شروع از +سلول +منو \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fi.xtb b/chromium/content/app/strings/translations/content_strings_fi.xtb index 774105b60d5..a3a22cf0588 100644 --- a/chromium/content/app/strings/translations/content_strings_fi.xtb +++ b/chromium/content/app/strings/translations/content_strings_fi.xtb @@ -2,22 +2,30 @@ tyhjä +valikkokohde mykistä ääniraita vaihtopainike Sekuntia Anna sähköpostiosoite. täydentävä +marquee +liukusäädin Poista viimeisimmät haut +valintaikkuna artikkeli banneri alue Tässä kuussa Valitse tiedostot +varausosoitin Kirjoita ei-tyhjä sähköpostiosoite. Muu... +ilmoitusvalintaikkuna +puuruudukko AP/IP toista etälaitteella Valitse +hahmotelma pp tila Arvon on oltava tai aiempi. @@ -26,8 +34,10 @@ Lyhennä tämä teksti alle merkkiin (tällä hetkellä käytössä merkkiä). Viikko , valintaruutu +päivämäärän ja ajan valitsin Päivä Täytä tämä kenttä. +välilehtiluettelo Ei viimeisimpiä hakuja toista Syötä kelvollinen arvo. Lähin kelvollinen arvo on . @@ -35,16 +45,25 @@ alaviite ääni Arvon on oltava tai myöhempi. +ponnahduspainike Valitse tämä ruutu jatkaaksesi. 1024 (keskitaso) +valintapainikeryhmä elokuvan nykyinen tila Anna pilkuilla erotettu sähköpostiosoitteiden luettelo. +klikata +kohde korostettu sisältö linkki Anna kelvollinen arvo. Kentän arvo on puutteellinen tai annettu päivä on virheellinen. +taulukko +huom. Anna URL-osoite. +ajastin +yhdistelmäruutu Arvon tulee olla pienempi tai yhtä suuri kuin . määritelmä +otsikko elokuvan jäljellä oleva aika sekunteina vaihto kk @@ -52,60 +71,89 @@ termi Pidennä tämä teksti yli merkkiin (tällä hetkellä käytössä merkkiä). -osaa ennen tulevassa osassa ei pitäisi olla merkkiä . +sovellus +vierityspalkki Millisekuntia +kuva Näytä edellinen kuukausi siirry +värinvalitsin +valikkopalkki +sisällön tiedot Syötä kelvollinen arvo. Kaksi lähintä kelvollista arvoa ovat ja . +valikkopainike Tiedot lomake +tree -osan jälkeen tulevassa osassa ei pitäisi olla merkkiä . +välilehtipaneeli video +haku Anna numero. valittu Lisää -osaa seuraava osa. on puutteellinen. +etenemisen osoitin luettelon merkitsijä Näytä seuraava kuukausi +rivin otsikko Arvon tulee olla suurempi tai yhtä suuri kuin . Lisää -osaa ennen tuleva osa. on puutteellinen. +Tallenna kuva +puukohde Valitse vähintään yksi tiedosto. poistu koko näytön tilasta poista valinta +päivämäärän valitsin peruuta mykistys +time keskeytä toisto +painike Muu... Tyhjennä 2048 (korkea taso) Viikko -osoite +pyöräytyspainike Vuosi +luetteloruutu peruuta ääniraidan mykistys aktivoi +mittari elokuvan aikajanan pikkukuva elokuvan ajan säätimen pikkukuva Näytä kuukaudenvalintapaneeli äänen ajan liukusäädin +ajan valitsin siirry koko näytön tilaan +loki Tyhjennä Viimeisimmät haut Joku muu profiili... askellin +viivoitin +työkaluvinkki Laajennuksen lataaminen epäonnistui. äänetön paina piilota tekstitykset +hakemisto +muotoiltu lainaus elokuvan aikajana Käytä pyydettyä muotoilua. kulunut aika Valitse kohde luettelosta. +sarakkeen otsikko Valitse tiedosto Muu... +sarkain Tuntia jäljellä oleva aika älä näytä tekstityksiä HTML-sisältö tauko +näyttämiskolmio Sähköpostiosoitteeseen kuuluu -osa. Osoitteesta puuttuu . +jakaja Ei valittua tiedostoa hakutekstikenttä näytä tekstitykset @@ -116,8 +164,11 @@ kuvakartta Virheellinen arvo. valitse +työkalupalkki tiedostoa Valitse tiedosto. +valintapainike +avattavan valikon painike Minuuttia median hallinta määritelmäluettelo @@ -127,10 +178,15 @@ aloita toisto pää Kuukausi +avata nykyinen toistoaika sekunteina navigointi näytä tekstitykset +dokumentti matematiikka Muu... +ilmoitus , alkupäivä: +solu +valikko \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fil.xtb b/chromium/content/app/strings/translations/content_strings_fil.xtb index 03a0a8e2f53..d62cc1970e1 100644 --- a/chromium/content/app/strings/translations/content_strings_fil.xtb +++ b/chromium/content/app/strings/translations/content_strings_fil.xtb @@ -2,22 +2,30 @@ blangko +item sa menu i-mute ang audio track button sa pag-toggle Segundo Mangyaring magpasok ng email address. complementary +marquee +slider Lisiman ang Kasalukuyang Mga Paghahanap +dialog article banner rehiyon Buwang ito Pumili ng Mga File +indicator kapag busy Mangyaring maglagay ng isang non-empty na email address. Iba pa... +alert_dialog +tree grid AM/PM i-play sa malayuang device piliin +balangkas dd katayuan Dapat o mas nauna ang value. @@ -26,8 +34,10 @@ Mangyaring paikliin ang tekstong ito ng (na) character o mas mababa (kasalukuyan kang gumagamit ng (na) character). Linggo , checkbox +picker ng petsa at oras Araw Pakipunan ang field na ito. +listahan ng tab Walang kamakailang mga paghahanap i-play Mangyaring maglagay ng isang wastong value. Ang pinakamalapit na wastong value ay . @@ -35,16 +45,25 @@ footer audio Dapat o mas bago ang value. +pop up na button Pakitingnan ang kahon na ito kung gusto mong magpatuloy. 1024 (Katamtamang Grado) +pangkat ng radyo kasalukuyang katayuan ng pelikula Mangyaring magpasok ng listahan ng email address na pinaghihiwalay ng kuwit. +i-click +object naka-highlight na content link Mangyaring maglagay ng wastong halaga. Hindi kumpleto ang field o may isang di-wastong petsa. +talahanayan +note Mangyaring magpasok ng URL. +timer +combo box Dapat mas mababa kaysa sa o katumbas ng ang halaga. kahulugan +heading bilang ng segundong natitira sa pelikula lumipat mm @@ -52,60 +71,89 @@ termino Pakihabaan ang text na ito hanggang (na) character o higit pa (kasalukuyan kang gumagamit ng (na) character). Hindi dapat naglalaman ng simbolong '' ang bahagi bago ang '.' +application +scroll bar Milliseconds +graphic Ipakita ang nakaraang buwan tumalon +tagapili ng kulay +menu bar +impormasyon ng content Mangyaring maglagay ng isang wastong value. Ang dalawang pinakamalapit na wastong value ay at . +button ng menu Mga Detalye form +tree Hindi dapat naglalaman ng simbolong '' ang bahagi pagkatapos ng '.' +panel ng tab video +search Mangyaring maglagay ng numero. ang napili Mangyaring maglagay ng isang bahagi pagkatapos ng '.' Hindi kumpleto ang '.' +indicator ng pag-usad Ilista ang marker Ipakita ang susunod na buwan +header ng row Dapat mas mataas kaysa sa o katumbas ng ang halaga. Mangyaring maglagay ng isang bahagi na sinusundan ng '.' Hindi kumpleto ang '.' +I-save anyo +item sa tree Mangyaring pumili ng isa o higit pang mga file. lumabas sa full screen i-uncheck +picker ng petsa i-unmute +oras i-pause ang pag-playback +button Iba pa... I-reset 2048 (Pinakamataas na Marka) Linggo -address +button ng pag-spin Taon +kahon ng listahan i-unmute ang audio track isaaktibo +metro thumb ng timeline ng pelikula thumb ng scrubber ng oras ng pelikula Ipakita ang panel ng pagpipilian ng buwan scrubber ng oras ng audio +picker ng petsa mag-full screen +log I-clear Kasalukuyang Mga Paghahanap Iba pa... stepper +ruler +tooltip Hindi ma-load ang plugin. i-mute pindutin itago ang mga nakasarang caption +direktoryo +blockquote oras ng pelikula Pakitugma ang hiniling na format. lumipas na oras Mangyaring pumili ng item sa listahan. +header ng column Pumili ng File Iba pa... +tab Oras natitirang oras ihinto ang pagpapakita ng mga nakasarang caption HTML na nilalaman i-pause +disclosure triangle Mangyaring magsama ng '' sa email address. Kulang ng '' ang '.' +splitter Walang napiling file text field ng paghahanap simulan ang pagpapakita ng mga nakasarang caption @@ -116,8 +164,11 @@ mapa ng imahe Di-wastong halaga. I-tsek +toolbar mga file Mangyaring pumili ng file. +radio button +button ng drop-down Minuto kontrol sa media listahan ng kahulugan @@ -127,10 +178,15 @@ simulan ang pag-playback main Buwan +buksan kasalukuyang oras ayon sa segundo navigation ipakita ang mga nakasarang caption +dokumento math Iba pa... +alerto , na magsisimula sa +cell +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fr.xtb b/chromium/content/app/strings/translations/content_strings_fr.xtb index cbdadaf1b56..4bd0f3abb36 100644 --- a/chromium/content/app/strings/translations/content_strings_fr.xtb +++ b/chromium/content/app/strings/translations/content_strings_fr.xtb @@ -2,22 +2,30 @@ vide +élément de menu désactiver le son de la piste audio bouton d'activation/de désactivation Secondes Veuillez saisir une adresse e-mail. complémentaire +marquee +curseur Effacer les recherches récentes +boîte de dialogue article bannière région Ce mois Sélect. fichiers +indicateur d'état "Occupé" Veuillez saisir une adresse e-mail dans le champ correspondant. Autre… +alert_dialog +arborescence AM/PM lire sur un appareil à distance sélectionner +vue d'ensemble jj état La date ou l'heure doit être égale ou antérieure à "". @@ -26,8 +34,10 @@ Veuillez réduire ce texte à  caractères maximum (il compte actuellement  caractères). Semaine , case à cocher +outil de sélection de la date et de l'heure Jour Veuillez renseigner ce champ. +liste d'onglets Aucune recherche récente lire Veuillez saisir une valeur valide. La valeur valide la plus proche est "". @@ -35,16 +45,25 @@ pied de page audio La date ou l'heure doit être égale ou postérieure à "". +bouton pop-up Veuillez cocher cette case si vous souhaitez continuer. 1024 (sécurité moyenne) +groupe de cases d'option état actuel du film Veuillez saisir une liste d'adresses e-mail séparées par une virgule. +cliquer +objet contenu en surbrillance Lien Veuillez saisir une valeur valide. Le champ n'est pas complet ou contient une date non valide. +tableau +remarque Veuillez saisir une URL. +minuteur +boîte combinée Cette valeur doit être inférieure ou égale à . définition +titre  nombre de secondes du film restantes interrupteur mm @@ -52,60 +71,89 @@ terme Veuillez allonger ce texte pour qu'il comporte au moins  caractères. Il en compte actuellement . La partie suivie du symbole "" ne doit pas contenir le caractère "". +application +barre de défilement Millisecondes +élément graphique Afficher le mois précédent accéder +palette couleurs +barre de menu +infos sur le contenu Veuillez saisir une valeur valide. Les deux valeurs valides les plus proches sont "" et "". +bouton de menu Détails formulaire +tree La partie précédée du symbole "" ne doit pas contenir le caractère "". +panneau des onglets vidéo +rechercher Veuillez saisir un nombre.  élément(s) sélectionné(s) Veuillez saisir la partie manquante après le symbole "". L'adresse "" est incomplète. +indicateur de progression marqueur de liste Afficher le mois suivant +en-tête de ligne Cette valeur doit être supérieure ou égale à . Veuillez saisir la partie manquante avant le caractère "". L'adresse "" est incomplète. +Enregistrer nombre +élément d'arborescence Veuillez sélectionner un ou plusieurs fichiers. quitter le mode plein écran décocher +outil de sélection de la date réactiver le son +horodatage interrompre la lecture +bouton Autre… Réinitialiser 2048 (haute sécurité) Semaine -adresse +bouton toupie Année +zone de liste réactiver le son de la piste audio activer +outil de mesure vignette de la chronologie du film vignette de la barre de défilement de la durée du film Afficher le panneau de sélection du mois curseur durée audio +outil de sélection de l'heure activer le mode plein écran +journal Effacer Recherches récentes Autre... curseur +règle +info-bulle Impossible de charger le plug-in. muet appuyer masquer les sous-titres +annuaire +bloc de citation durée du film Veuillez respecter le format requis. temps écoulé Sélectionnez un élément dans la liste. +en-tête de colonne Choisissez un fichier Autre… +tabulation Heures temps restant ne plus afficher les sous-titres Contenu HTML pause +triangle d'expansion Veuillez inclure "" dans l'adresse e-mail. Il manque un symbole "" dans "". +séparateur Aucun fichier choisi champ de recherche de texte commencer à afficher les sous-titres @@ -116,8 +164,11 @@ image map Valeur incorrecte cocher +barre d'outils  fichiers Veuillez sélectionner un fichier. +case d'option +bouton déroulant Minutes commande multimédia liste de définitions @@ -127,10 +178,15 @@ commencer la lecture principal Mois +ouvrir durée actuelle en secondes navigation afficher les sous-titres +document math. Autre… +alerte (premier jour de la semaine : ) +cellule +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_gu.xtb b/chromium/content/app/strings/translations/content_strings_gu.xtb index 7f07e911954..f0547f017d9 100644 --- a/chromium/content/app/strings/translations/content_strings_gu.xtb +++ b/chromium/content/app/strings/translations/content_strings_gu.xtb @@ -2,22 +2,30 @@ ખાલી +મેનૂ આઇટમ ઑડિઓ ટ્રેક મ્યૂટ કરો ટોગલ બટન સેકંડ કૃપા કરી કોઈ ઇમેઇલ સરનામું દાખલ કરો. પૂરક +માર્કી +સ્લાઇડર હાલની શોધને સાફ કરો +સંવાદ લેખ બેનર પ્રદેશ આ મહિને ફાઇલો પસંદ કરો +વ્યસ્ત સૂચક કૃપા કરીને એક બિન-ખાલી ઇમેઇલ સરનામું દાખલ કરો. અન્ય... +ચેતવણી_સંવાદ +ટ્રી ગ્રિડ AM/PM રિમોટ ઉપકરણ પર ચલાવો પસંદ કરો +રૂપરેખા dd સ્થિતિ મૂલ્ય અથવા પહેલાંનું હોવું આવશ્યક છે. @@ -26,8 +34,10 @@ કૃપા કરીને આ ટેક્સ્ટને અક્ષર અથવા તેથી ઓછા સુધી નાનો કરો (તમે હાલમાં અક્ષરોનો ઉપયોગ કરી રહ્યા છો). અઠવાડિયું , ચેકબોક્સ +તારીખ અને સમય પીકર દિવસ કૃપા કરીને આ ફીલ્ડ ભરો. +ટેબ સૂચિ હાલની શોધો નથી ચલાવો કૃપા કરીને એક માન્ય મૂલ્ય દાખલ કરો. નિકટતમ માન્ય મૂલ્ય છે. @@ -35,16 +45,25 @@ ફૂટર ઑડિઓ મૂલ્ય અથવા પછીનું હોવું આવશ્યક છે. +પોપ અપ બટન જો તમે આગળ વધવા માંગતા હો તો કૃપા કરીને આ બૉક્સને ચેક કરો. 1024 (મધ્યમ ગ્રેડ) +રેડિઓ જૂથ વર્તમાન મૂવીની સ્થિતિ કૃપા કરીને અલ્પવિરામથી વિભાજિત ઇમેઇલ સરનામાંઓની સૂચિ દાખલ કરો. +ક્લિક કરો +ઑબ્જેક્ટ હાઇલાઇટ કરેલ સામગ્રી લિંક કૃપા કરીને માન્ય કિંમત દાખલ કરો. ફીલ્ડ અપૂર્ણ છે અથવા અમાન્ય તારીખ ધરાવે છે. +કોષ્ટક +નોંધ કૃપા કરી કોઈ URL દાખલ કરો. +ટાઇમર +કૉમ્બો બૉક્સ મૂલ્ય જેટલું અથવા આનાથી ઓછું હોવું આવશ્યક છે. વિવરણ +મથાળું ofmovie ની બાકી સેકન્ડ્સની સંખ્યા સ્વિચ mm @@ -52,60 +71,89 @@ ટર્મ કૃપા કરીને આ ટેક્સ્ટને અક્ષર અથવા તેથી વધુ સુધી લંબાવો (તમે હાલમાં અક્ષરોનો ઉપયોગ કરી રહ્યાં છો). '' દ્વારા અનુસરાઈ રહેલા ભાગમાં '' પ્રતીક શામેલ હોવું જોઈએ નહીં. +ઍપ્લિકેશન +સ્ક્રોલ બાર મીલીસેકન્ડ +ગ્રાફિક પહેલાનો મહિનો દર્શાવો જંપ કરો +રંગ ચૂંટનાર +મેનૂ બાર +સામગ્રી માહિતી કૃપા કરીને એક માન્ય મૂલ્ય દાખલ કરો. બે નિકટતમ માન્ય મૂલ્યો અને છે. +મેનૂ બટન વિગતો ફોર્મ +ટ્રી '' ને અનુસરી રહેલા ભાગમાં '' પ્રતીક શામેલ હોવું જોઈએ નહીં. +ટેબ પેનલ વિડિઓ +search કૃપા કરીને એક નંબર દાખલ કરો. પસંદ કર્યા કૃપા કરીને '' ને અનુસરી રહેલો ભાગ દાખલ કરો. '' અપૂર્ણ છે. +પ્રગતિ સૂચક સૂચિ માર્કર આગલો મહિનો દર્શાવો +પંક્તિ હેડર મૂલ્ય જેટલું અથવા આનાથી વધુ હોવું આવશ્યક છે. કૃપા કરીને '' ની આગળનો ભાગ દાખલ કરો. '' અપૂર્ણ છે. +સાચવો આકૃતિ +ટ્રી આઇટમ કૃપા કરીને એક અથવા વધુ ફાઇલ પસંદ કરો. પૂર્ણ સ્ક્રીનથી બહાર નીકળો અનચેક કરો +તારીખ પીકર અનમ્યૂટ કરો +સમય પ્લેબેક થોભાવો +બટન અન્ય... રીસેટ કરો 2048 (ઉચ્ચ ગ્રેડ) અઠવાડિયું -સરનામું +સ્પિન બટન વર્ષ +સૂચિ બૉક્સ ઑડિઓ ટ્રેક અનમ્યૂટ કરો સક્રિય કરો +મીટર મૂવી સમયરેખા થમ્બ મૂવી સમય સ્ક્રબર થમ્બ મહિના પસંદગી પેનલ દર્શાવો ઑડિઓ સમય સ્ક્રબર +સમય પીકર પૂર્ણ સ્ક્રીનમાં દાખલ થાઓ +લૉગ સાફ કરો તાજેતરની શોધ અન્ય... સ્ટેપર +આંકણી +ટૂલટીપ પ્લગિન લોડ કરી શક્યાં નથી. બંધ કરો દબાવો ઉપશીર્ષક છુપાવો +નિર્દેશિકા +બ્લૉકક્વોટ મૂવીનો સમય કૃપા કરીને વિનંતી કરેલા ફોર્મેટ સાથે મેળ કરો. વીતેલો સમય કૃપા કરીને સૂચિમાંથી એક આઇટમ પસંદ કરો. +કૉલમ હેડર ફાઇલ પસંદ કરો અન્ય... +ટેબ કલાક બાકીનો સમય વિગતવાર ઉપશીર્ષકનું પ્રદર્શન અટકાવો HTML સામગ્રી થોભો +પ્રકટીકરણ ત્રિકોણ કૃપા કરીને ઇમેઇલ સરનામાંમાં '' શામેલ કરો. '' માં '' ખૂટી રહ્યું છે. +વિભાજનકર્તા કોઈ ફાઇલ પસંદ કરેલી નથી ટેક્સ્ટ ફીલ્ડ શોધો ઉપશીર્ષક પ્રદર્શન પ્રારંભ કરો @@ -116,8 +164,11 @@ છબી નકશો અમાન્ય મૂલ્ય. તપાસો +ટુલબાર ફાઇલો કૃપા કરીને કોઈ ફાઇલ પસંદ કરો. +રેડિઓ બટન +ડ્રોપ ડાઉન બટન મિનિટ મીડિયાનું નિયંત્રણ વિવરણ સૂચિ @@ -127,10 +178,15 @@ પ્લેબૅક શરૂ કરો મુખ્ય મહિનો +ખોલો સેકન્ડ્સમાં વર્તમાન સમય નેવિગેશન ઉપશીર્ષક બતાવો +દસ્તાવેજ ગણિત અન્ય... +ચેતવણી , થી શરૂ કરીને +કોષ +મેનૂ \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hi.xtb b/chromium/content/app/strings/translations/content_strings_hi.xtb index cfc4011ba87..0f1b2549285 100644 --- a/chromium/content/app/strings/translations/content_strings_hi.xtb +++ b/chromium/content/app/strings/translations/content_strings_hi.xtb @@ -2,22 +2,30 @@ खाली +मेनू आइटम ऑडियो ट्रैक म्यूट करें टॉगल बटन सेकंड कृपया ई-मेल पता दर्ज करें. पूरक +मार्की +स्लाइडर हाल ही की खोजें साफ़ करें +संवाद लेख बैनर क्षेत्र इस माह फ़ाइलें चुनें +व्यस्त संकेतक कृपया गैर-खाली ईमेल पता डालें. अन्य... +सूचना संवाद +ट्री ग्रिड पूर्वाह्न/अपराह्न दूरस्थ डिवाइस पर चलाएं चुनें +बाह्यरेखा dd स्थिति मान या पहले का होना चाहिए. @@ -26,8 +34,10 @@ कृपया इस टेक्स्ट को वर्णों या कम तक छोटा करें (वर्तमान में आप वर्णों का उपयोग कर रहे हैं). सप्ताह , चेकबॉक्स +दिनांक और समय पिकर दिन कृपया इस फ़ील्ड को भरें. +टैब सूची हाल ही कोई खोज नहीं चलाएं कृपया कोई मान्य मान डालें. निकटतम मान्य मान है. @@ -35,16 +45,25 @@ पाद लेख ऑडियो मान या बाद का होना चाहिए. +पॉप अप बटन यदि आप आगे बढ़ना चाहते हैं तो इस बॉक्‍स को चेक करें. 1024 (मध्यम ग्रेड) +रेडियो समूह फ़िल्म की वर्तमान स्थिति कृपया ईमेल पतों की अल्पविराम द्वारा विभाजित सूची दर्ज करें. +क्लिक करें +ऑब्जेक्ट हाइलाइट की गई सामग्री संपर्क कृपया मान्य मान डालें. फ़ील्ड अधूरी है या उसमें एक अमान्य दिनांक है. +तालिका +नोट कृपया URL लिखें. +टाइमर +कॉम्बो बॉक्स मान से कम या इसके बराबर होना चाहिए. परिभाषा +शीर्षक फ़िल्म के शेष सेकंड स्विच करें mm @@ -52,60 +71,89 @@ शब्द कृपया इस लेख को वर्णों या अधिक तक बढ़ाएं (वर्तमान में आप वर्णों का उपयोग कर रहे हैं). '' के बाद आने वाले भाग में '' प्रतीक शामिल नहीं होना चाहिए. +ऐप्लिकेशन +स्क्रॉल बार मिलीसेकंड +ग्राफ़‍िक पिछला माह दिखाएं जाएं +रंग पिकर +मेनू बार +सामग्री की जानकारी कृपया कोई मान्य मान डालें. दो निकटतम मान्य मान और हैं. +मेनू बटन विवरण फ़ॉर्म +ट्री '' के बाद आने वाले भाग में '' प्रतीक शामिल नहीं होना चाहिए. +टैब फलक वीडियो +search कृपया कोई संख्या डालें. चुने गए कृपया '' के बाद आने वाला भाग डालें. '' अधूरा है. +प्रगति संकेतक सूची चिन्हक अगला माह दिखाएं +पंक्ति शीर्षलेख मान से कम या इसके बराबर होना चाहिए. कृपया '' के पहले वाला भाग डालें. '' अधूरा है. +सहेजें आकृति +ट्री आइटम कृपया एक या अधिक फ़ाइल को चुनें. पूर्ण स्क्रीन से बाहर निकलें अनचेक करें +दिनांक पिकर अनम्यूट करें +समय प्लेबैक रोकें +बटन अन्य... रीसेट करें 2048 (उच्च ग्रेड) सप्ताह -पता +स्पिन बटन वर्ष +सूची बॉक्स ऑडियो ट्रैक अनम्यूट करें सक्रिय करें +मीटर फ़िल्म टाइमलाइन झलक फ़िल्म समय स्क्रबर झलक माह चयन फलक दिखाएं ऑडियो समय स्क्रबर +समय पिकर पूर्ण स्क्रीन में प्रवेश करें +लॉग साफ़ करें हाल ही में की गई खोजें अन्य... स्टेपर +पैमाना +टूलटिप प्लग इन लोड नहीं किया जा सका. म्यूट करें दबाएं बंद कैप्शन छिपाएं +निर्देशिका +ब्लॉककोट फि़ल्म का समय कृपया अनुरोधित प्रारूप का मिलान करें. बीता हुआ समय कृपया सूची में किसी आइटम को चुनें. +स्तंभ शीर्षलेख फ़ाइल चुनें अन्य... +टैब घंटे शेष समय बंद कैप्शन दिखाना रोकें HTML सामग्री पॉज़ करें +प्रकटीकरण त्रिकोण कृपया ईमेल पते में '' शामिल करें. '' में '' नहीं है. +विभाजक कोई फाइल नहीं चुनी गई लेख फ़ील्ड खोजें बंद कैप्शन दिखाना प्रारंभ करें @@ -116,8 +164,11 @@ चित्र मानचित्र अमान्य मान. चेक करें +टूलबार फ़ाइल कृपया किसी फ़ाइल को चुनें. +रेडियो बटन +ड्रॉप डाउन बटन मिनट मीडिया नियंत्रण परिभाषा सूची @@ -127,10 +178,15 @@ प्लेबैक शुरू करें मुख्य माह +खोलें वर्तमान समय, सेकंड में मार्गदर्शक बंद कैप्शन दिखाएं +दस्तावेज़ गणित अन्य... +सूचना , से प्रारंभ हो रहा है +सेल +मेनू \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hr.xtb b/chromium/content/app/strings/translations/content_strings_hr.xtb index 42bab11b76f..ee21500f683 100644 --- a/chromium/content/app/strings/translations/content_strings_hr.xtb +++ b/chromium/content/app/strings/translations/content_strings_hr.xtb @@ -2,22 +2,30 @@ prazno +stavka izbornika isključivanje zvučnog zapisa preklopni gumb Sekunde Unesite e-adresu. dopunski +pomični tekst +klizač Obriši najnovija pretraživanja +dijalog članak natpis regija Ovaj mjesec Odabir datoteka +pokazivač zauzetosti Unesite e-adresu koja nije prazna vrijednost. Drugo... +dijaloški okvir upozorenja +rešetka u obliku stabla prijepodne/poslijepodne reproduciraj na udaljenom uređaju odaberi +obris dd status Vrijednost mora biti ili prije toga. @@ -26,8 +34,10 @@ Skratite taj tekst na znakova ili manje (trenutačno upotrebljavate znakova). . tjedan, . godina potvrdni okvir +alat za odabir datuma i vremena Dan Ispunite ovo polje. +popis kartica Nema najnovijih pretraživanja reprodukcija Unesite važeću vrijednost. Najbliža je važeća vrijednost . @@ -35,16 +45,25 @@ podnožje zvuk Vrijednost mora biti ili nakon toga. +gumb skočnog prozora Označite taj okvir ako želite ići dalje. 1024 (srednji) +grupa izbornih gumba trenutačan status filma Unesite popis adresa e-pošte odijeljen zarezima. +klikanje +objekt istaknuti sadržaj veza Unesite važeću vrijednost. Ovo je polje nepotpuno ili sadrži nevažeći datum. +tablica +napom Unesite URL. +odbrojavanje +kombinirani okvir Vrijednost mora biti ili manja. definicija +naslov preostali broj sekundi filma prebaci mm @@ -52,60 +71,89 @@ pojam Produljite broj znakova u tekstu na minimalno . Trenutačno imate premalo znakova (). Dio adrese ispred znaka "" ne smije sadržavati simbol "". +aplikacija +klizač Milisekunde +slika Prikaži prethodni mjesec skoči +odabir boja +traka izbornika +informacije o sadržaju Unesite važeću vrijednost. Dvije su najbliže važeće vrijednosti i . +gumb izbornika Detalji obrazac +stablo Dio adrese iza znaka "" ne smije sadržavati simbol "". +ploča kartice videozapis +search Unesite broj. Odabrano: Unesite dio adrese iza znaka "". "" nije potpuna e-adresa. +pokazivač napretka oznaka popisa Prikaži sljedeći mjesec +zaglavlje retka Vrijednost mora biti ili veća. Unesite dio adrese ispred znaka "". "" nije potpuna e-adresa. +Spremi lik +stavka stabla Izaberite jednu ili više datoteka. izlazak iz cijelog zaslona ukloni oznaku +alat za odabir datuma uključivanje zvuka +vrijeme pauziranje reprodukcije +gumb Drugo... Ponovno postavi 2048 (visoki stupanj) Tjedan -adresa +okretni gumb Godina +okvir s popisom uključivanje zvučnog zapisa aktiviraj +mjerač gumb na vremenskom klizaču filma gumb vremenskog klizača filma Prikaži ploču za odabir mjeseca klizač vremena audiozapisa +alat za odabir vremena otvaranje na cijelom zaslonu +zap Izbriši Najnovija pretraživanja Ostalo... povećanje/smanjenje strelicama gore/dolje (stepper) +ravnalo +opis Nije bilo moguće učitati dodatak. isključi ton pritisni sakrivanje titlova +direktorij +uvučeni citat vrijeme filma Udovoljite zadanom formatu. proteklo vrijeme Odaberite stavku s popisa. +zaglavlje stupca Odaberi datoteku Drugo... +kart Sati preostalo vrijeme zaustavljanje prikazivanja titlova HTML sadržaj pauziraj +trokut za otkrivanje Uključite znak "" u e-adresu. U adresi "" nedostaje znak "". +razdjelnik Nije odabrana niti jedna datoteka. pretraži tekstno polje početak prikazivanja titlova @@ -116,8 +164,11 @@ karta slika Nevažeća vrijednost. označi +alatna traka Broj datoteka: Odaberite datoteku. +izborni gumb +gumb padajućeg izbornika Minute kontrola medija popis definicija @@ -127,10 +178,15 @@ početak reprodukcije glav Mjesec +otvaranje trenutačno vrijeme u sekundama navigacija prikazivanje titlova +dokument mat Drugo... +upozorenje , počevši od +ćelija +izbornik \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hu.xtb b/chromium/content/app/strings/translations/content_strings_hu.xtb index f2ad7a60824..5fa02530d00 100644 --- a/chromium/content/app/strings/translations/content_strings_hu.xtb +++ b/chromium/content/app/strings/translations/content_strings_hu.xtb @@ -2,22 +2,30 @@ üres +menüelem hangsáv némítása átkapcsológomb Másodperc Kérjük, adjon meg egy e-mail címet. kiegészítő +fényújság +csúszka Friss keresések törlése +párbeszédpanel cikk szalaghirdetés régió Ebben a hónapban Fájlok kiválasztása +„elfoglalt” kijelző Kérjük, ne hagyja üresen az e-mail-cím mezőjét. Más... +értesítő párbeszédpanel +farács de./du. lejátszás távoli eszközön Kiválasztás +áttekintés nn állapot Az érték vagy azt megelőző kell, hogy legyen. @@ -26,8 +34,10 @@ Kérjük, rövidítse le a szöveget legfeljebb karakterre (jelenleg karaktert használ). . hét, jelölőnégyzet +dátum- és időválasztó nap Kérjük, töltse ki ezt a mezőt. +laplista Nincsenek friss keresések lejátszás Kérjük, érvényes értéket adjon meg. A legközelebbi érvényes érték . @@ -35,16 +45,25 @@ lábléc audio Az érték vagy azt követő kell, hogy legyen. +előugró gomb Kérjük, jelölje be ezt a jelölőnégyzetet, ha tovább kíván haladni. 1024 (Közepes) +választógombcsoport a jelenlegi film állapota Kérjük, adjon meg egy vesszőkkel elválasztott e-mail címlistát. +kattintás +objektum kijelölt tartalom link Kérjük, adjon meg érvényes értéket. A mező hiányos, vagy érvénytelen dátum van megadva. +táblázat +jegyzet Adjon meg egy URL-t. +időzítő +legördülő lista Az érték legyen kisebb vagy egyenlő, mint . definíció +. címsor a filmből hátralévő másodpercek váltás hh @@ -52,60 +71,89 @@ kifejezés Kérjük, karakter hosszú vagy annál hosszabb szöveget adjon meg (jelenleg karaktert használ). A „” előtti rész nem tartalmazhat „” karaktert. +alkalmazás +görgetősáv Ezredmásodperc +grafika Az előző hónap megjelenítése Mehet +színválasztó +menüsor +tartalominformáció Kérjük, érvényes értéket adjon meg. A két legközelebbi érvényes érték és . +menügomb Részletek űrlap +fa A „” utáni rész nem tartalmazhat „” karaktert. +lappanel video +search Kérjük, adjon meg egy számot. kiválasztva Kérjük, adja meg a „” utáni részt is. A(z) „” cím nem teljes. +folyamatjelző listajelölő A következő hónap megjelenítése +sorfejléc Az érték legyen nagyobb vagy egyenlő, mint . Kérjük, adja meg a „” előtti részt is. A „” cím nem teljes. +Mentés alak +faelem Kérjük, válasszon ki egy vagy több fájlt. kilépés a teljes képernyős nézetből Megjelölés eltávolítása +dátumválasztó némítás feloldása +idő lejátszás szüneteltetése +gomb Más... Visszaállítás 2048 (magasfokú) Hét -cím +léptetőnyíl Év +listamező hangsáv némításának feloldása Aktiválás +mérő film idővonalának indexképe a filmidővezérlő indexképe A hónapválasztási panel megjelenítése hang idővonalának vezérlője +időválasztó teljes képernyős nézet +napló Törlés Friss keresések Egyéb... léptető +vonalzó +elemleírás Nem sikerült betölteni a beépülő modult. némítás Gomb lenyomása feliratok elrejtése +címtár +bekezdésszintű idézet film ideje Kérjük, tartsa magát a kívánt formátumhoz. eltelt idő Kérjük, válasszon egyet a lista elemei közül. +oszlopfejléc Fájl kiválasztása Más... +tab Óra hátralévő idő feliratok elrejtése HTML-tartalom szüneteltetés +plusz tartalom kibontására szolgáló háromszög Kérjük, írjon egy „” karaktert az e-mail címbe. A(z) „” címből hiányzik a „” jel. +felosztó Nincs fájl kiválasztva keresés a szövegmezőben feliratok megjelenítése @@ -116,8 +164,11 @@ interaktív kép Érvénytelen érték. Megjelölés +eszköztár fájl Válasszon egy fájlt. +választógomb +legördülő gomb Perc médiavezérlő definíciós lista @@ -127,10 +178,15 @@ lejátszás indítása hónap +megnyitás jelenlegi idő másodpercben navigáció feliratok megjelenítése +dokumentum matematika Más... +értesítés . hét (-i dátummal kezdődik) +cella +menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_id.xtb b/chromium/content/app/strings/translations/content_strings_id.xtb index 8f0b1f71390..61a611a49b7 100644 --- a/chromium/content/app/strings/translations/content_strings_id.xtb +++ b/chromium/content/app/strings/translations/content_strings_id.xtb @@ -2,22 +2,30 @@ kosong +item menu bisukan trek audio tombol beralih Detik Masukkan alamat email. komplementer +marquee +penggeser Hapus Penelusuran Barusan +dialog article spanduk wilayah Bulan ini Pilih File +indikator sibuk Jangan mengosongkan bidang alamat email. Lainnya... +alert_dialog +kisi pohon AM/PM putar di perangkat jarak jauh pilih +garis batas hh status Tanggal harus atau lebih awal. @@ -26,8 +34,10 @@ Pendekkan teks ini menjadi karakter atau kurang (saat ini Anda menggunakan karakter). Minggu , kotak centang +pemilih tanggal dan waktu Hari Harap isi bidang ini. +daftar tab Tidak ada penelusuran terkini main Masukkan nilai yang valid. Nilai valid terdekatnya adalah . @@ -35,16 +45,25 @@ footer audio Tanggal harus atau setelahnya. +tombol munculan Centang kotak ini jika Anda ingin melanjutkan. 1024 (Tingkat Menengah) +grup radio status film saat ini Masukkan daftar alamat email yang dipisahkan dengan koma. +klik +objek konten yang disorot tautan Masukkan nilai yang valid. Bidang tersebut tidak lengkap atau memiliki tanggal yang tidak valid. +tabel +note Masukkan URL. +pewaktu +kotak kombo Nilai harus lebih kecil atau sama dengan . definisi +judul sisa waktu film dalam detik saklar bb @@ -52,60 +71,89 @@ istilah Perpanjang teks ini hingga karakter atau lebih (saat ini Anda menggunakan karakter). Bagian sebelum '' tidak boleh berisi simbol ''. +aplikasi +bilah gulir Milidetik +grafis Tampilkan bulan sebelumnya lompati +pemilih warna +bilah menu +info konten Masukkan nilai yang valid. Dua nilai valid terdekat adalah dan . +tombol menu Detail formulir +tree Bagian setelah '' tidak boleh berisi simbol ''. +panel tab video +search Masukkan nomor. dipilih Masukkan bagian setelah ''. '' tidak lengkap. +indikator kemajuan penanda daftar Tampilkan bulan berikutnya +judul baris Nilai harus lebih besar daripada atau sama dengan . Masukkan bagian yang diikuti dengan ''. '' tidak lengkap. +Simpan sosok +item pohon Pilih salah satu atau beberapa file. keluar dari tampilan layar penuh batalkan centang +pemilih tanggal suarakan +waktu jeda pemutaran +tombol Lainnya... Setel ulang 2048 (Tingkat Tinggi) Minggu -alamat +tombol putar Tahun +kotak daftar suarakan trek audio aktifkan +pengukur jempol garis waktu film jempol scrubber waktu film Tampilkan panel pilihan bulan scrubber waktu audio +pemilih waktu masuk layar penuh +log Hapus Penelusuran Barusan Lainnya... stepper +penggaris +keterangan alat Tidak dapat memuat plugin. bisukan tekan sembunyikan teks +direktori +blockquote waktu film Sesuaikan dengan format yang diminta. waktu berlalu Pilih item pada daftar. +judul kolom Pilih File Lainnya... +tab Jam sisa waktu berhenti menampilkan teks Konten HTML jeda +segitiga pengungkapan Sertakan '' pada alamat email. '' tidak memiliki ''. +pemisah Tidak ada file yang dipilih bidang teks penelusuran mulai menampilkan teks @@ -116,8 +164,11 @@ gambar peta Nilai tidak valid. centangi +bilah alat file Pilih file. +tombol radio +tombol tarik-turun Menit kontrol media daftar definisi @@ -127,10 +178,15 @@ mulai pemutaran main Bulan +buka waktu saat ini dalam detik navigasi tampilkan teks +dokumen math Lainnya... +lansiran , dimulai pada +sel +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_it.xtb b/chromium/content/app/strings/translations/content_strings_it.xtb index 6327156cb3d..b282d4a9e8d 100644 --- a/chromium/content/app/strings/translations/content_strings_it.xtb +++ b/chromium/content/app/strings/translations/content_strings_it.xtb @@ -2,22 +2,30 @@ vuoto +voce di menu disattiva traccia audio pulsante di attivazione/disattivazione Secondi Inserisci un indirizzo email. elementi complementari +marquee +dispositivo di scorrimento Cancella ricerche recenti +finestra di dialogo articolo banner regione Questo mese Scegli file +indicatore di occupato Inserisci un indirizzo email valido. Altro... +alert_dialog +griglia ad albero AM/PM riproduci su dispositivo remoto seleziona +contorno gg stato Il valore deve essere o precedente. @@ -26,8 +34,10 @@ Riduci questo testo a caratteri o meno (attualmente stai utilizzando caratteri). Settimana , casella di controllo +selettore di data e ora Giorno Compila questo campo. +elenco schede Nessuna ricerca recente riproduci Inserisci un valore valido. Il valore valido più vicino è . @@ -35,16 +45,25 @@ piè di pagina audio Il valore deve essere o successivo. +pulsante popup Seleziona questa casella se intendi procedere. 1024 (Medium Grade) +gruppo pulsanti di opzione stato corrente del filmato Inserisci un elenco di indirizzi email separati da virgola. +fai clic +oggetto contenuti evidenziati link Inserisci un valore valido. Il campo è incompleto o presenta una data non valida. +tabella +nota Inserisci un URL. +timer +casella combinata Il valore deve essere inferiore o uguale a . definizione +intestazione numero di secondi di filmato rimanenti strumento per il passaggio ad altri elementi mm @@ -52,60 +71,89 @@ termine Prolunga questo testo a o più caratteri (al momento stai utilizzando caratteri). Una parte seguita da "" non deve contenere il simbolo "". +applicazione +barra di scorrimento Millisecondi +immagine Mostra mese precedente vai +selettore colori +barra dei menu +informazioni sui contenuti Inserisci un valore valido. I due valori validi più vicini sono e . +pulsante di menu Dettagli modulo +tree Una parte che segue "" non deve contenere il simbolo "". +riquadro a schede video +ricerca Inserisci un numero. Elementi selezionati: Inserisci una parte dopo "". Il valore "" è incompleto. +indicatore di avanzamento indicatore elenco Mostra mese successivo +intestazione di riga Il valore deve essere superiore o uguale a . Inserisci una parte seguita da "". Il valore "" è incompleto. +Salva figura +elemento albero Seleziona uno o più file. esci da schermo intero deseleziona +selettore di data riattiva audio +time sospendi riproduzione +pulsante Altro... Ripristina 2048 (alta qualità) Settimana -indirizzo +pulsante di selezione Anno +casella di riepilogo riattiva traccia audio attiva +indicatore cursore sequenza temporale filmato cursore dispositivo di scorrimento tempo filmato Mostra il riquadro di selezione del mese dispositivo di scorrimento durata audio +selettore di ora passa a schermo intero +log Cancella Ricerche recenti Altro... stepper +righello +descrizione comando Impossibile caricare il plug-in disattiva audio premi nascondi sottotitoli +directory +citazione lunga tempo filmato Rispetta il formato richiesto. tempo trascorso Seleziona un elemento nell'elenco. +intestazione di colonna Scegli file Altro... +Tab Orario tempo rimanente interrompi la visualizzazione dei sottotitoli Contenuti HTML pausa +triangolo descrittivo Aggiungi un simbolo "" nell'indirizzo email. In "" manca un simbolo "". +barra di divisione Nessun file selezionato campo di testo della ricerca avvia la visualizzazione dei sottotitoli @@ -116,8 +164,11 @@ image map Valore non valido. seleziona +barra degli strumenti file Seleziona un file. +pulsante di opzione +pulsante a discesa Minuti controllo contenuti multimediali elenco di definizioni @@ -127,10 +178,15 @@ inizia riproduzione main Mese +apri tempo attuale in secondi navigazione mostra sottotitoli +documento elemento matematico Altro... +avviso , a partire dal giorno +cella +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_iw.xtb b/chromium/content/app/strings/translations/content_strings_iw.xtb index bafc1c8c3f6..fcb26542c37 100644 --- a/chromium/content/app/strings/translations/content_strings_iw.xtb +++ b/chromium/content/app/strings/translations/content_strings_iw.xtb @@ -2,22 +2,30 @@ ריק +פריט בתפריט השתק רצועת אודיו לחצן החלפה שניות הזן כתובת אימייל. משלים +marquee +מחוון הסר חיפושים אחרונים +דו-שיח article מודעת באנר אזור החודש בחר קבצים +סימן ויזואלי 'לא פנוי/ה' הזן ערך נדרש בשדה של כתובת האימייל. אחר... +alert_dialog +רשת של עץ AM/PM הפעלה במכשיר מרוחק בחר +קו מתאר dd מצב על הערך להיות או מוקדם יותר. @@ -26,8 +34,10 @@ קצר טקסט זה ל- תווים או פחות (אתה משתמש כעת ב- תווים). שבוע ‏, תיבת סימון +בוחר תאריך ושעה יום מלא שדה זה. +רשימת כרטיסיות אין חיפושים אחרונים הפעל הזן ערך חוקי. הערך החוקי הקרוב ביותר הוא @@ -35,16 +45,25 @@ כותרת תחתונה אודיו על הערך להיות ומעלה. +לחצן קופץ סמן תיבה זו אם אתה רוצה להמשיך. 1024 (Medium Grade) +קבוצת לחצני בחירה מצב סרט נוכחי הזן רשימה של כתובות אימייל המופרדות באמצעות פסיקים. +לחץ +אובייקט תוכן מודגש קישור הזן ערך חוקי. השדה ריק או שהוא מכיל תאריך לא חוקי. +טבלה +note הזן כתובת אתר. +טיימר +תיבה משולבת הערך חייב להיות קטן מ- או שווה לו. הגדרה +כותרת מספר שניות שנותרו עד לסיום הסרט מעבר mm @@ -52,60 +71,89 @@ מונח הארך טקסט זה ל- תווים או יותר (אתה משתמש כרגע ב- תווים). חלק ולאחריו '' לא אמור לכלול את הסמל ''. +יישום +סרגל גלילה אלפיות שנייה +פריט גרפי הצג חודש קודם קפוץ +color picker +שורת תפריטים +פרטי תוכן הזן ערך חוקי. שני הערכים החוקיים הקרובים ביותר הם ו-. +לחצן תפריט פרטים טופס +tree חלק ולאחריו '' לא אמור לכלול את הסמל ''. +חלונית כרטיסיות סרטוני וידאו +Search הזן מספר. נבחרו הזן חלק ולאחריו ''‏. השדה '' אינו מלא. +סימן ויזואלי להתקדמות סמן רשימה הצג את החודש הבא +כותרת שורה הערך חייב להיות גדול מ- או שווה לו. הזן חלק ולאחריו ''‏. השדה '' אינו מלא. +שמור ספרה +פריט בעץ בחר קובץ אחד או יותר. צא ממסך מלא בטל סימון +בוחר תאריכים בטל השתקה +time השהה הפעלה +לחצן אחר... אפס 2048 (High Grade) שבוע -כתובת +לחצן קביעת ערך שנה +תיבת רשימה בטל השתקה של רצועת אודיו הפעל +מד תמונה ממוזערת של משך סרט תמונה ממוזערת של מסתיר זמן סרט הצג חלונית לבחירת חודש מסתיר משך אודיו +בוחר שעות היכנס למסך מלא +log נקה חיפושים אחרונים אחר... פקד חצים +סרגל +הסבר קצר לא ניתן היה לטעון את הפלאגין. השתק לחץ הסתר כתוביות סגורות +ספריה +‏רכיב blockquote משך הסרט התאם את הפורמט המבוקש. זמן שחלף בחר פריט מהרשימה. +כותרת עמודה בחר קובץ אחר... +tab שעות זמן שנותר הפסק להציג כתוביות סגורות ‏תוכן HTML השהה +משולש הצגה/הסתרה כלול '' בכתובת האימייל. ב-'' חסר ''. +מפצל לא נבחר קובץ שדה טקסט לחיפוש התחל להציג כתוביות סגורות @@ -116,8 +164,11 @@ מפת תמונות ערך לא חוקי. סמן +סרגל כלים קבצים בחר קובץ. +לחצן בחירה +לחצן רשימה נפתחת דקות שליטה במדיה רשימת הגדרות @@ -127,10 +178,15 @@ התחל בהפעלה main חודש +פתח זמן נוכחי בשניות ניווט הצג כתוביות סגורות +מסמך math אחר... +התראה , שמתחיל בתאריך +תא +תפריט \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ja.xtb b/chromium/content/app/strings/translations/content_strings_ja.xtb index 949545d1610..dc5d484da6a 100644 --- a/chromium/content/app/strings/translations/content_strings_ja.xtb +++ b/chromium/content/app/strings/translations/content_strings_ja.xtb @@ -2,22 +2,30 @@ 空白 +メニュー項目 音声トラックをミュート 切り替えボタン メール アドレスを入力してください。 捕捉 +マーキー +スライダ 最近の検索履歴を消去 +ダイアログ 記事 バナー 地域 今月 ファイル選択 +ビジー インジケータ メール アドレスを入力してください。 その他... +アラート ダイアログ +ツリーグリッド AM/PM リモート デバイスで再生 選択 +アウトライン ステータス 以前の値を指定する必要があります。 @@ -26,8 +34,10 @@ このテキストを半角 文字以下にしてください(現時点で半角 文字です)。 年第 チェックボックス +日時選択ツール このフィールドを入力してください。 +タブリスト 最近の検索はありません 再生 有効な値を入力してください。有効な値として最も近いのは です。 @@ -35,16 +45,25 @@ フッター 音声 以降の値を指定する必要があります。 +ポップアップ ボタン 次に進むにはこのチェックボックスをオンにしてください。 1024 (中) +ラジオボタン グループ 現在の映画のステータス メール アドレスのカンマ区切りリストを入力してください。 +クリック +オブジェクト ハイライト表示されたコンテンツ リンク 有効な値を入力してください。フィールドの入力が不完全か、日付が無効です。 + +注記 URL を入力してください。 +タイマー +コンボボックス 値は 以下にする必要があります。 定義 +見出し 残り時間(秒) 切り替え @@ -52,60 +71,89 @@ 用語 このテキストは 文字以上で指定してください(現在は 文字です)。 」の前の文字列に記号「」を使用しないでください。 +アプリケーション +スクロールバー ミリ秒 +グラフィック 前の月を表示 ジャンプ +色の選択 +メニューバー +コンテンツ情報 有効な値を入力してください。有効な値として最も近いのは です。 +メニューボタン 詳細 フォーム +ツリー 」に続く文字列に記号「」を使用しないでください。 +タブパネル 動画 +検索 数字を入力してください。 件選択 」に続く文字列を入力してください。「」は完全なメール アドレスではありません。 +プログレスバー リスト マーカー 次の月を表示 +行見出し 値は 以上にする必要があります。 」の前の文字列を入力してください。「」は完全なメール アドレスではありません。 +保存 +ツリー項目 1 つ以上のファイルを選択してください。 全画面表示を終了 チェックを外す +日付選択ツール ミュートを解除 +日時 再生を一時停止 +ボタン その他... リセット 2048 (高) -アドレス +スピンボタン +リストボックス 音声トラックのミュートを解除 アクティブにする +メーター タイムラインのサムネイル シークバーのサムネイル 月選択パネルを表示 オーディオ再生バー +時間選択ツール 全画面表示 +ログ クリア 最近の検索 その他... ステッパー +ルーラー +ツールチップ プラグインを読み込むことができませんでした。 ミュート 押す クローズド キャプションを非表示 +ディレクトリ +引用 上映時間 指定されている形式で入力してください。 経過時間 リスト内の項目を選択してください。 +列見出し ファイルを選択 その他... +タブ 時間 残り時間 クローズド キャプションの表示を終了 HTML コンテンツ 一時停止 +三角形の展開ボタン メール アドレスに「」を挿入してください。「」内に「」がありません。 +分割バー 選択されていません 検索テキスト欄 クローズド キャプションの表示を開始 @@ -116,8 +164,11 @@ イメージ マップ 値が無効です。 チェックを付ける +ツールバー ファイル ファイルを選択してください。 +ラジオボタン +プルダウン ボタン メディア管理 定義リスト @@ -127,10 +178,15 @@ 再生を開始 本文 +開く 現在の時間(秒単位) ナビゲーション クローズド キャプションを表示 +ドキュメント 数式 その他... +アラート から始まる +セル +メニュー \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_kn.xtb b/chromium/content/app/strings/translations/content_strings_kn.xtb index 3b132cc07e6..27ae0ef8fc7 100644 --- a/chromium/content/app/strings/translations/content_strings_kn.xtb +++ b/chromium/content/app/strings/translations/content_strings_kn.xtb @@ -2,22 +2,30 @@ ಖಾಲಿ +ಮೆನು ಐಟಂ ಆಡಿಯೋ ಟ್ರ್ಯಾಕ್ ಮ್ಯೂಟ್ ಮಾಡು ಟಾಗಲ್ ಬಟನ್ ಸೆಕೆಂಡುಗಳು ದಯವಿಟ್ಟು ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ. ಪೂರಕವಾಗಿ +ಮಾರ್ಕ್ಯೂ +ಸ್ಲೈಡರ್ ಇತ್ತೀಚಿನ ಹುಡುಕಾಟವನ್ನು ತೆರವುಗೊಳಿಸಿ +ಸಂವಾದ ಲೇಖನ ಬ್ಯಾನರ್ ಪ್ರದೇಶ ಈ ತಿಂಗಳು ಫೈಲ್‌ಗಳನ್ನು ಆರಿಸಿ +ಕಾರ್ಯನಿರತ ಸೂಚಕ ಖಾಲಿ-ಅಲ್ಲದ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ. ಇತರೆ... +alert_dialog +ಟ್ರೀ ಗ್ರಿಡ್ AM/PM ರಿಮೋಟ್ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ ಆಯ್ಕೆ ಮಾಡಿ +ಔಟ್‌ಲೈನ್ ದಿದಿ ಸ್ಥಿತಿ ಮೌಲ್ಯವು ಅಥವಾ ಹಿಂದಿನದ್ದಾಗಿರಬೇಕು. @@ -26,8 +34,10 @@ ದಯವಿಟ್ಟು ಈ ಪಠ್ಯವನ್ನು ಅಕ್ಷರಗಳಿಗೆ ಅಥವಾ ಅದಕ್ಕಿಂತಲೂ ಕಡಿಮೆಗೆ ಸೀಮಿತಗೊಳಿಸಿ (ನೀವು ಪ್ರಸ್ತುತ ಅಕ್ಷರಗಳನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ). ವಾರ , ಚೆಕ್‌ಬಾಕ್ಸ್ +ದಿನಾಂಕ ಮತ್ತು ಸಮಯ ಪಿಕರ್ ದಿನ ದಯವಿಟ್ಟು ಈ ಕ್ಷೇತ್ರವನ್ನು ಭರ್ತಿ ಮಾಡಿ. +ಟ್ಯಾಬ್ ಪಟ್ಟಿ ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು ಇಲ್ಲ ಪ್ಲೇ ಮಾಡಿ ಮಾನ್ಯ ಮೌಲ್ಯವನ್ನು ನಮೂದಿಸಿ. ಹತ್ತಿರದ ಮಾನ್ಯ ಮೌಲ್ಯವು ಆಗಿದೆ. @@ -35,16 +45,25 @@ ಅಡಿಟಿಪ್ಪಣಿ ಆಡಿಯೋ ಮೌಲ್ಯವು ಅಥವಾ ನಂತರದ್ದಾಗಿರಬೇಕು. +ಪಾಪ್ ಅಪ್ ಬಟನ್ ನೀವು ಮುಂದುವರೆಯಬೇಕಾದರೆ ದಯವಿಟ್ಟು ಈ ಬಾಕ್ಸ್ ಅನ್ನು ಪರೀಕ್ಷಿಸಿ. 1024 (ಮದ್ಯಮ ಶ್ರೇಣಿ) +ರೇಡಿಯೋ ಗುಂಪು ಪ್ರಸ್ತುತ ಚಲನಚಿತ್ರದ ಸ್ಥಿತಿ ದಯವಿಟ್ಟು ಅಲ್ಪ ವಿರಾಮದಿಂದ ಬೇರ್ಪಡಿಸಿದ ಇಮೇಲ್ ವಿಳಾಸಗಳ ಪಟ್ಟಿಯನ್ನು ನಮೂದಿಸಿ. +ಕ್ಲಿಕ್‌ ಮಾಡಿ +ವಸ್ತು ಹೈಲೈಟ್ ಮಾಡಲಾದ ವಿಷಯ ಲಿಂಕ್ ದಯವಿಟ್ಟು ಮಾನ್ಯ ಮೌಲ್ಯವನ್ನು ನಮೂದಿಸಿ. ಕ್ಷೇತ್ರವು ಅಪೂರ್ಣವಾಗಿದೆ ಅಥವಾ ಅಮಾನ್ಯ ದಿನಾಂಕವನ್ನು ಹೊಂದಿದೆ. +ಕೋಷ್ಟಕ +ಟಿಪ್ಪಣಿ ದಯವಿಟ್ಟು URL ಅನ್ನು ನಮೂದಿಸಿ. +ಟೈಮರ್ +ಕಾಂಬೊ ಬಾಕ್ಸ್ ಮೌಲ್ಯವು ಕ್ಕೆ ಸಮನಾಗಿರಬಹುದು ಅಥವಾ ಅದಕ್ಕಿಂತಲೂ ಕಡಿಮೆ ಆಗಿರಬಹುದು. ವಿವರಣೆ +ಶಿರೋನಾಮೆ ಚಲನಚಿತ್ರ ಬಾಕಿ ಉಳಿದಿರುವ ಸೆಕುಂಡುಗಳ ಸಂಖ್ಯೆ ಸ್ವಿಚ್ ಮಿಮೀ @@ -52,60 +71,89 @@ ನಿಯಮ ಈ ಪಠ್ಯವನ್ನು ಅಕ್ಷರಗಳಿಗೆ ಅಥವಾ ಅದಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಿಗೆ ಸೀಮಿತಗೊಳಿಸಿ (ನೀವು ಪ್ರಸ್ತುತವಾಗಿ ಅಕ್ಷರಗಳನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ). '' ನಂತರದ ಭಾಗವು '' ಚಿಹ್ನೆಯನ್ನು ಒಳಗೊಂಡಿರಬಾರದು. +ಅಪ್ಲಿಕೇಶನ್ +ಸ್ಕ್ರಾಲ್ ಪಟ್ಟಿ ಮಿಲಿಸೆಕೆಂಡುಗಳು +ಗ್ರಾಫಿಕ್ ಹಿಂದಿನ ತಿಂಗಳು ತೋರಿಸು ಹಾರು +ಬಣ್ಣದ ಆಯ್ಕೆಮಾಡುವಿಕೆ +ಮೆನು ಬಾರ್‌ +ವಿಷಯ ಮಾಹಿತಿ ಮಾನ್ಯವಾದ ಮೌಲ್ಯವನ್ನು ನಮೂದಿಸಿ. ಮತ್ತು ಎರಡು ಹತ್ತಿರದ ಮಾನ್ಯ ಮೌಲ್ಯಗಳಾಗಿವೆ. +ಮೆನು ಬಟನ್ ವಿವರಗಳು ಫಾರ್ಮ್ +ಮರ '' ನಂತರದ ಭಾಗವು '' ಚಿಹ್ನೆಯನ್ನು ಒಳಗೊಂಡಿರಬಾರದು. +ಟ್ಯಾಬ್ ಫಲಕ ವೀಡಿಯೊ +search ದಯವಿಟ್ಟು ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ. ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ '' ನಂತರದ ಒಂದು ಭಾಗವನ್ನು ನಮೂದಿಸಿ. '' ಅಪೂರ್ಣವಾಗಿದೆ. +ಪ್ರಗತಿ ಸೂಚಕ ಪಟ್ಟಿ ಗುರುತು ಮುಂದಿನ ತಿಂಗಳು ತೋರಿಸಿ +ಸಾಲಿನ ಶಿರೋನಾಮೆ ಮೌಲ್ಯವು ಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಾಗಿರಬೇಕು ಅಥವಾ ಸಮನಾಗಿರಬೇಕು. '' ನಂತರದ ಭಾಗವನ್ನು ನಮೂದಿಸಿ. '' ಅಪೂರ್ಣವಾಗಿದೆ. +ಉಳಿಸು ಆಕೃತಿ +ಟ್ರೀ ಐಟಂ ದಯವಿಟ್ಟು ಒಂದು ಅಥವಾ ಹೆಚ್ಚು ಫೈಲ್‌ಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ. ಪೂರ್ಣಪರದೆಯಿಂದ ನಿರ್ಗಮಿಸು ಪರೀಕ್ಷಿಸಬೇಡಿ +ದಿನಾಂಕದ ಪಿಕರ್ ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡು +ಸಮಯ ಪ್ಲೇಬ್ಯಾಕ್ ವಿರಾಮಗೊಳಿಸು +ಬಟನ್ ಇತರೆ... ಮರುಹೊಂದಿಸು 2048 (ಉನ್ನತ ಶ್ರೇಣಿ) ವಾರ -ವಿಳಾಸ +ಸ್ಪಿನ್ ಬಟನ್ ವರ್ಷ +ಪಟ್ಟಿಯ ಬಾಕ್ಸ್ ಆಡಿಯೋ ಟ್ರ್ಯಾಕ್ ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡು ಸಕ್ರಿಯಗೊಳಿಸು +ಮೀಟರ್ ಚಲನಚಿತ್ರ ಸಮಯಮಿತಿಯ ಥಂಬ್‌ ಚಲನಚಿತ್ರ ಸಮಯ ಸ್ಕ್ರಬ್ಬರ್ ಥಂಬ್ ತಿಂಗಳ ಆಯ್ಕೆ ಪ್ಯಾನಲ್ ತೋರಿಸಿ ಆಡಿಯೊ ಸಮಯ ಸ್ಕ್ರಬ್ಬರ್ +ಸಮಯ ಪಿಕರ್ ಪೂರ್ಣ ಪರದೆ ನಮೂದಿಸು +ಲಾಗ್ ತೆರವುಗೊಳಿಸಿ ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು ಇತರೆ... ಸ್ಟೀಪರ್ +ರೂಲರ್‌ +ಟೂಲ್‌ ಟಿಪ್‌ ಪ್ಲಗ್-ಇನ್ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಮ್ಯೂಟ್ ಒತ್ತಿ ಮುಚ್ಚಿರುವ ಶೀರ್ಷಿಕೆಗಳನ್ನು ಮರೆಮಾಡಿ +ಡೈರೆಕ್ಟರಿ +ಬ್ಲಾಕ್‌ಕೋಟ್ ಚಲನಚಿತ್ರ ಸಮಯ ದಯವಿಟ್ಟು ವಿನಂತಿಸಿದ ಸ್ವರೂಪವನ್ನು ಹೊಂದಿಸಿ. ಕಳೆದುಹೋದ ಸಮಯ ಪಟ್ಟಿಯಲ್ಲಿನ ಐಟಂ ಅನ್ನು ದಯವಿಟ್ಟು ಆಯ್ಕೆ ಮಾಡಿ. +ಕಾಲಮ್ ಶಿರೋನಾಮೆ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿ ಇತರೆ... +ಟ್ಯಾಬ್ ಗಂಟೆಗಳು ಉಳಿದಿರುವ ಸಮಯ ಮುಚ್ಚಲಾಗಿರುವ ಶೀರ್ಷಿಕೆಗಳ ಪ್ರದರ್ಶಿಸುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಿ HTML ವಿಷಯ ವಿರಾಮ +ತ್ರಿಕೋನ ಪ್ರಕಟಣೆ ಇಮೇಲ್ ವಿಳಾಸದಲ್ಲಿ ಒಂದು '' ಅನ್ನು ಸೇರಿಸಿ. '' ನಲ್ಲಿ '' ಕಾಣೆಯಾಗಿದೆ. +ಛೇದಕ ಯಾವುದೇ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿಲ್ಲ ಪಠ್ಯ ಕ್ಷೇತ್ರವನ್ನು ಹುಡುಕಿ ಮುಚ್ಚಲಾಗಿರುವ ಶೀರ್ಷಿಕೆಗಳ ಪ್ರದರ್ಶಿಸುವಿಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ @@ -116,8 +164,11 @@ ಇಮೇಜ್ ನಕ್ಷೆ ಅಮಾನ್ಯ ಮೌಲ್ಯ. ಪರಿಶೀಲಿಸು +ಟೂಲ್‌ಬಾರ್ ಫೈಲ್‌ಗಳು ದಯವಿಟ್ಟು ಫೈಲ್ ಆಯ್ಕೆಮಾಡಿ. +ರೇಡಿಯೋ ಬಟನ್ +ಡ್ರಾಪ್ ಡೌನ್ ಬಟನ್ ನಿಮಿಷಗಳು ಮಾಧ್ಯಮ ನಿಯಂತ್ರಣ ವಿವರಣೆ ಪಟ್ಟಿ @@ -127,10 +178,15 @@ ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭಿಸಿ ಮುಖ್ಯ ತಿಂಗಳು +ತೆರೆ ಸೆಕೆಂಡ್‌ಗಳಲ್ಲಿ ಪ್ರಸ್ತುತ ಸಮಯ ನ್ಯಾವಿಗೇಷನ್ ಮುಚ್ಚಿರುವ ಶೀರ್ಷಿಕೆಗಳನ್ನು ತೋರಿಸಿ +ಡಾಕ್ಯುಮೆಂಟ್ ಮ್ಯಾಥ್ ಇತರೆ... +ಎಚ್ಚರಿಕೆ , ರಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ +ಸೆಲ್ +ಮೆನು \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ko.xtb b/chromium/content/app/strings/translations/content_strings_ko.xtb index ad076ebfa53..71c58a10e83 100644 --- a/chromium/content/app/strings/translations/content_strings_ko.xtb +++ b/chromium/content/app/strings/translations/content_strings_ko.xtb @@ -2,22 +2,30 @@ 비어 있음 +메뉴 항목 오디오 트랙 음소거 전환 버튼 이메일 주소를 입력하세요. 상호 보완 +marquee +슬라이더 최근 검색 삭제 +대화상자 article 배너 지역 이번 달 파일 선택 +바쁨 표시기 비어 있지 않은 이메일 주소를 입력해 주세요. 다른 일자... +알림 대화상자 +트리 격자 오전/오후 원격 기기에서 재생 선택 +개요 상태 값은 이전이어야 합니다. @@ -26,8 +34,10 @@ 이 텍스트를 자 이하로 줄이세요(현재 자 사용 중). , 번째 주 체크박스 +날짜 및 시간 선택기 이 입력란을 작성하세요. +탭 목록 최근 수행된 검색 없음 재생 유효한 값을 입력해 주세요. 가장 근접한 유효 값은 입니다. @@ -35,16 +45,25 @@ 바닥글 오디오 값은 이후여야 합니다. +팝업 버튼 계속하려면 이 확인란을 선택하세요. 1024(중간 등급) +라디오 그룹 현재 영화 상태 이메일 주소를 쉼표로 구분하여 입력하세요. +클릭 +객체 강조표시된 콘텐츠 링크 유효한 값을 입력하세요. 입력을 완료하지 않았거나 날짜가 잘못되었습니다. + +note URL을 입력하세요. +타이머 +콤보 상자 값은 이하여야 합니다. 정의 +제목 영화 남은 시간(초) 전환 @@ -52,60 +71,89 @@ 용어 이 텍스트를 자 이상으로 늘리세요(현재 자 사용 중). '' 앞 부분에 '' 기호가 포함되면 안됩니다. +애플리케이션 +스크롤바 밀리초 +그래픽 이전 달 표시 건너뛰기 +color picker +메뉴 표시줄 +콘텐츠 정보 유효한 값을 입력해 주세요. 가장 근접한 유효 값 2개는 입니다. +메뉴 버튼 세부정보 양식 +tree '' 다음 부분에 '' 기호가 포함되면 안됩니다. +탭 패널 동영상 +검색 숫자를 입력하세요. 개 선택됨 '' 뒷 부분을 입력해 주세요. ''(이)가 완전하지 않습니다. +진행률 표시기 목록 표시기 다음 달 표시 +행 헤더 값은 이상이어야 합니다. '' 앞 부분을 입력해 주세요. ''(이)가 완전하지 않습니다. +저장 그림 +트리 항목 파일을 한 개 이상 선택하세요. 전체화면 닫기 선택취소 +날짜 선택기 음소거 해제 +time 재생 일시중지 +버튼 다른 시간... 초기화 2048(높은 등급) -주소 +스핀 버튼 연도 +목록 상자 오디오 트랙 음소거 해제 활성화 +미터 영화 타임라인 썸 영화 시간 스크러버 썸 월 선택 패널 표시 오디오 시간 스크러버 +시간 선택기 전체화면 열기 +log 삭제 최근 수행된 검색 기타... 증감자 +눈금자 +도움말 플러그인을 로드할 수 없습니다. 음소거 누르기 캡션 숨기기 +디렉토리 +인용구 영화 시간 요청한 형식과 일치시키세요. 경과 시간 목록에서 항목을 선택하세요. +열 헤더 파일 선택 다른 주... +tab 시간 남은 시간 캡션 표시 중지 HTML 콘텐츠 일시중지 +펼치기/접기 삼각형 이메일 주소에 ''를 포함해 주세요. ''에 ''가 없습니다. +분할선 선택된 파일 없음 검색어 입력란 캡션 표시 시작 @@ -116,8 +164,11 @@ 이미지 지도 값이 잘못되었습니다. 선택 +툴바 파일 파일을 선택하세요. +라디오 버튼 +드롭다운 버튼 미디어 컨트롤 정의 목록 @@ -127,10 +178,15 @@ 재생 시작 main +열기 현재 시간(초) 탐색 캡션 표시 +문서 math 다른 달... +알림 (에 시작) + +메뉴 \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_lt.xtb b/chromium/content/app/strings/translations/content_strings_lt.xtb index 21642cdd3a9..acc8d3632b3 100644 --- a/chromium/content/app/strings/translations/content_strings_lt.xtb +++ b/chromium/content/app/strings/translations/content_strings_lt.xtb @@ -2,22 +2,30 @@ tuščia +meniu elementas nutildyti garso įrašo takelį perjungimo mygtukas Sekundės Įveskite el. pašto adresą. papildomas +marquee +šliaužiklis Išvalyti pastarąsias paieškas +dialogo langas artikelis reklamjuostė regionas Šis mėnuo Pasirinkti failus +užimtumo indikatorius Įveskite el. pašto adresą (nepalikite lauko tuščio). Kita... +alert_dialog +medžio tinklelis iki pietų / po pietų leisti naudojant nuotolinį įrenginį pasirinkti +kontūras dd būsena Vertė turi būti ar ankstesnė data. @@ -26,8 +34,10 @@ Sutrumpinkite šį tekstą iki simb. ar mažiau (šiuo metu naudojate simb.). m. sav. žymimasis laukelis +datos ir laiko rinkiklis Diena Užpildykite šį lauką. +skirtukų sąrašas Pastaruoju metu paieškų nevykdyta paleisti Įveskite tinkamą vertę. Artimiausia tinkama vertė yra . @@ -35,16 +45,25 @@ poraštė garso įrašas Vertė turi būti ar vėlesnė data. +Iššokantysis mygtukas Jei norite tęsti, pažymėkite šį laukelį. 1024 (vidutinio lygio) +akučių grupė dabartinė filmo būsena Įveskite kableliais atskirtą el. pašto adresų sąrašą. +spustelėti +objektas paryškintas turinys nuoroda Įveskite tinkamą vertę. Laukas užpildytas nevisiškai arba įvesta netinkama data. +lentelė +pastaba Įveskite URL. +laikmatis +jungtinis laukelis Vertė turi būti arba mažesnė. apibrėžimas + antraštė likusių filmo sekundžių skaičius jungiklis mm @@ -52,60 +71,89 @@ terminas Pailginkite šį tekstą iki simb. ar daugiau (šiuo metu naudojate simb.). Prieš „“ esančioje dalyje neturėtų būti simbolio „“. +programa +slinkties juosta Milisekundės +grafinis elementas Rodyti ankstesnį mėnesį peršokti +spalvos parinkiklis +meniu juosta +turinio informacija Įveskite galiojančią vertę. Dvi artimiausios vertės yra ir . +meniu mygtukas Išsami informacija forma +tree Po „“ esančioje dalyje neturėtų būti simbolio „“. +skirtuko skydelis vaizdo įrašas +ieškoti Įveskite skaičių. Pasirinkta: Įveskite dalį po „“. „“ nėra visas el. pašto adresas. +eigos indikatorius sąrašo žymeklis Rodyti kitą mėnesį +eilutės antraštė Vertė turi būti arba didesnė. Įveskite el. pašto adreso dalį iki „“. „“ nėra visas el. pašto adresas. +Išsaugoti iliustracija +medžio elementas Pasirinkite bent vieną failą. išeiti iš viso ekrano režimo Nuimti žymėjimą +datos rinkiklis įjungti garsą +time pristabdyti atkūrimą +mygtukas Kita... Nustatyti iš naujo 2048 (Aukšto laipsnio) Savaitė -adresas +sukimo mygtukas Metai +sąrašo laukelis įjungti garso įrašo takelio garsą aktyvinti +matuoklis filmo laiko juostos miniatiūra filmo laiko valdiklio miniatiūra Rodyti mėnesio pasirinkimo skydelį garso laiko valdiklis +laiko rinkiklis įjungti viso ekrano režimą +log Išvalyti Naujausios paieškos Kita... ėjimo aukštyn / žemyn valdiklis +liniuotė +patarimas Nepavyko įkelti papildinio. nutildyti paspausti slėpti subtitrus +katalogas +citata filmo laikas Priderinkite reikalaujamą formatą. praėjęs laikas Pasirinkite sąraše pateiktą elementą. +stulpelio antraštė Pasirinkti failą Kita... +Tabuliavimo klavišas Valandos likęs laikas nebepateikti subtitrų HTML turinys pristabdyti +paskelbimo trikampis Į el. pašto adresą įtraukite „“. „“ trūksta „“. +skaidiklis Nepasirinktas joks failas paieškos teksto laukas pateikti subtitrus @@ -116,8 +164,11 @@ paveikslėlio žemėlapis Neteisinga vertė. tikrinti +įrankių juosta failai (-ų) Pasirinkite failą. +akutė +išskleidžiamasis mygtukas Minutės medijos valdiklis apibrėžimų sąrašas @@ -127,10 +178,15 @@ pradėti atkūrimą main Mėnuo +atidaryti dabartinis laikas sekundėmis naršymas rodyti subtitrus +dokumentas matematika Kita... +įspėjimas , nuo +langelis +meniu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_lv.xtb b/chromium/content/app/strings/translations/content_strings_lv.xtb index 358d1e0b5a4..2a47086d44c 100644 --- a/chromium/content/app/strings/translations/content_strings_lv.xtb +++ b/chromium/content/app/strings/translations/content_strings_lv.xtb @@ -2,22 +2,30 @@ tukšs +izvēlnes vienums izslēgt audio ieraksta skaņu pārslēgšanas poga Sekundes Ievadiet e-pasta adresi. papildu +slīdošais teksts +slīdnis Dzēst nesenos meklējumus +dialoglodziņš raksts reklāmkarogs reģions Šis mēnesis Izvēlēties failus +aizņemtības indikators Lūdzu, ievadiet e-pasta adresi. Cits... +brīdinājuma dialoglodziņš +koka režģis priekšpusdienā/pēcpusdienā atskaņot attālinātā ierīcē Atlasiet +kontūra dd statuss Vērtībai ir jābūt “” vai agrākam datumam vai laikam. @@ -26,8 +34,10 @@ Lūdzu, saīsiniet šo tekstu līdz vai mazāk zīmēm (pašreiz tas ietver rakstzīmes). . nedēļa, . gads izvēles rūtiņa +datuma un laika atlasītājs Diena Aizpildiet šo lauku. +ciļņu saraksts Nav nesenu meklējumu atskaņot Lūdzu, ievadiet derīgu vērtību. Tuvākā derīgā vērtība ir . @@ -35,16 +45,25 @@ kājene audio Vērtībai ir jābūt “” vai vēlākam datumam vai laikam. +uznirstoša poga Lai turpinātu, atzīmējiet šo izvēles rūtiņu. 1024 (Vidēja Atzīme) +radiopogu grupa pašreizējais filmas statuss Lūdzu, ievadiet ar komatu atdalītu e-pasta adrešu sarakstu. +noklikšķināt +objekts iezīmētais saturs saite Lūdzu, ievadiet derīgu vērtību. Šis lauks nav pabeigts, vai tajā ir norādīts nederīgs datums. +tabula +piezīme Ievadiet URL. +taimeris +kombinētais lodziņš Vērtībai ir jābūt mazākai vai vienādai ar . definīcija +. virsraksts atlikušais filmas laiks sekundēs slēdzis mm @@ -52,60 +71,89 @@ vārds Lūdzu, papildiniet šo tekstu līdz vismaz  rakstzīmēm (pašlaik tas ietver  rakstzīmes). Daļā, kas atrodas pirms zīmes , nedrīkst būt ietverts simbols . +lietojumprogramma +ritjosla Milisekundes +grafiskais elements Rādīt iepriekšējo mēnesi lekt +krāsu izvēle +izvēļņu josla +satura informācija Lūdzu, ievadiet derīgu vērtību. Divas tuvākās derīgās vērtības ir  un . +izvēlnes poga Informācija veidlapa +koks Daļā, kas atrodas aiz zīmes , nedrīkst būt ietverts simbols . +ciļņu panelis video +meklēt Lūdzu, ievadiet skaitli. Atlasīti  Lūdzu, ievadiet daļu, kas atrodas aiz zīmes . “” ir nepilna adrese. +norises indikators sarakstu marķieris Rādīt nākamo mēnesi +rindas virsraksts Vērtībai ir jābūt lielākai vai vienādai ar . Lūdzu, ievadiet daļu, kas atrodas pirms zīmes . “” ir nepilna adrese. +Saglabāt cipars +koka vienums Lūdzu, atlasiet vienu vai vairākus failus. iziet no pilnekrāna režīma neprbaudt +datuma atlasītājs rādīt +laiks pauzēt atskaņošanu +poga Cits... Atiestatīt 2048 (Augsta Atzīme) Nedēļa -adrese +skaitītājpoga Gads +sarakstlodziņš ieslēgt audio ieraksta skaņu aktivizt +mērītājs filmas laika skalas sīktēls filmas laika skalas slīdņa sīktēls Rādīt mēneša atlases paneli audio laika skalas slīdnis +laika atlasītājs atvērt pilnekrāna režīmā +žurnāls Notīrīt Neseni meklējumi Cits pārslēdzējs +lineāls +rīka padoms Nevarēja ielādēt spraudni. izslēgt skaņu nospiest slēpt slēgtos parakstus +katalogs +citāta bloks filmas laiks Pieskaņojiet vērtību prasītajam formātam. pagājušais laiks Atlasiet vienumu sarakstā. +slejas virsraksts Izvēlēties failu Cits... +cilne Stundas atlikušais laiks apturēt slēgto parakstu rādīšanu HTML saturs pauzēt +satura atklāšanas trijstūris E-pasta adresē ietveriet zīmi . Adresē “” trūkst zīmes . +sadalītājs Nav izvēlēts neviens fails meklēšanas teksta lauks sākt slēgto parakstu rādīšanu @@ -116,8 +164,11 @@ attēlu karte Nederīga vērtība. prbaudt +rīkjosla  faili Lūdzu, atlasiet failu. +radiopoga +nolaižamās izvēlnes poga Minūtes multivides vadība definīciju saraksts @@ -127,10 +178,15 @@ sākt atskaņošanu galvenais Mēnesis +atvērt pašreizējais laiks sekundēs navigācija rādīt slēgtos parakstus +dokuments matemātiska izteiksme Cits... +brīdinājums , sākot no: +šūna +izvēlne \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ml.xtb b/chromium/content/app/strings/translations/content_strings_ml.xtb index 491d1ad7f85..a8800d4ca95 100644 --- a/chromium/content/app/strings/translations/content_strings_ml.xtb +++ b/chromium/content/app/strings/translations/content_strings_ml.xtb @@ -2,22 +2,30 @@ ശൂന്യം +മെനു ഇനം ഓഡിയോ ട്രാക്ക് നിശബ്‌ദമാക്കുക ടോഗിൾ ബട്ടൺ സെക്കൻഡ് ദയവായി ഒരു ഇമെയില്‍ വിലാസം നല്‍കുക. കോംപ്ലിമെന്ററി +മാർക്യൂ +സ്ലൈഡർ അടുത്തിടെയുള്ള തിരയലുകള്‍ മായ്ക്കുക +ഡയലോഗ് ലേഖനം ബാനർ പ്രദേശം ഈ മാസം ഫയലുകൾ തിരഞ്ഞെടുക്കുക +'ബിസി ഇൻഡിക്കേറ്റർ' ശൂന്യമായിടാതെ, ഇമെയിൽ വിലാസം നൽകുക. മറ്റുള്ളവ... +അലേർട്ട്_ഡയലോഗ് +ട്രീ ഗ്രിഡ് AM/PM വിദൂര ഉപകരണത്തിൽ പ്ലേ ചെയ്യുക തിരഞ്ഞെടുക്കൂ +ഔട്ട്‌ലൈൻ തീയതി നില മൂല്യം എന്നതോ അതിനുമുമ്പുള്ള തീയതിയോ ആയിരിക്കണം. @@ -26,8 +34,10 @@ ഈ വാചകത്തെ അല്ലെങ്കില്‍ അതില്‍‌ക്കുറവ് പ്രതീകങ്ങളായി ദയവായി കുറയ്ക്കുക (നിങ്ങള്‍ നിലവില്‍ പ്രതീകങ്ങള്‍ ഉപയോഗിക്കുകയാണ്). ആഴ്‌ച , ചെക്ക്‌ബോക്‌സ് +തീയതിയും സമയ പിക്കറും ദിവസം ദയവായി ഈ ഫീല്‍ഡ് പൂരിപ്പിക്കുക. +ടാബ് ലിസ്റ്റ് സമീപകാല തിരയലുകള്‍ ഇല്ല പ്ലേ ചെയ്യുക സാധുവായ ഒരു മൂല്യം നൽകുക. ഏറ്റവുമടുത്ത സാധുവായ മൂല്യം ആണ്. @@ -35,16 +45,25 @@ അടിക്കുറിപ്പ് ഓഡിയോ മൂല്യം എന്നതോ അതിനുശേഷമുള്ള തീയതിയോ ആയിരിക്കണം. +പോപ്പ് അപ്പ് ബട്ടൺ നിങ്ങള്‍ തുടരാന്‍ താല്‍പ്പര്യപ്പെടുന്നെങ്കില്‍ ഈ ബോക്സ് ദയവായി പരിശോധിക്കുക. 1024 (മീഡിയം ഗ്രേഡ്) +റേഡിയോ ഗ്രൂപ്പ് നിലവിലെ മൂവി നില ഇമെയില്‍ വിലാസങ്ങളുടെ കോമയാല്‍ വേര്‍തിരിച്ച ഒരു പട്ടിക ദയവായി നല്‍കുക. +ക്ലിക്കുചെയ്യുക +ഒബ്‌ജക്‌റ്റ് ഹൈലൈറ്റുചെയ്‌തിരിക്കുന്ന ഉള്ളടക്കം ലിങ്ക് ഒരു സാധുതയുള്ള മൂല്യം നൽകുക. ഫീൽഡ് പൂർണ്ണമല്ല അല്ലെങ്കിൽ അസാധുവായ തീയതിയിലുള്ളതാണ്. +പട്ടിക +note ദയവായി ഒരു URL നല്‍കുക. +ടൈമർ +കോമ്പോ ബോക്‌സ് മൂല്യം എന്നതില്‍ കുറവോ സമമോ ആയിരിക്കണം. നിർവചനം +തലക്കെട്ട് മൂവിയുടെ അവശേഷിക്കുന്ന നിമിഷങ്ങളുടെ എണ്ണം സ്വിച്ച് മാസം @@ -52,72 +71,104 @@ പദം ഈ ടെക്‌സ്റ്റിന്റെ ദൈർഘ്യം അല്ലെങ്കിൽ അതിൽക്കൂടുതൽ പ്രതീകങ്ങൾ നൽകി കൂട്ടുക. (നിങ്ങൾ നിലവിൽ പ്രതീകങ്ങളാണ് ഉപയോഗിക്കുന്നത്). '' എന്നതിനുശേഷം വരുന്ന ഒരു ഭാഗത്തിൽ '' ചിഹ്നം ഉണ്ടാകരുത്. +അപ്ലിക്കേഷൻ +സ്‌ക്രോൾ ബാർ മില്ലിസെക്കൻഡ് +ഗ്രാഫിക് മുൻ മാസം കാണിക്കുക jump +വർണ്ണ പിക്കർ +മെനു ബാർ +ഉള്ളടക്ക വിവരം സാധുവായ മൂല്യം നൽകുക. സാധുവായ ഏറ്റവുമടുത്ത രണ്ട് മൂല്യങ്ങൾ , എന്നിവയാണ്. -വിശദാംശങ്ങള്‍‌ +മെനു ബട്ടൺ +വിശദാംശങ്ങൾ‌ ഫോം +ട്രീ '' എന്നതിനുശേഷം വരുന്ന ഭാഗത്തിൽ '' ചിഹ്നം ഉണ്ടാകരുത്. +ടാബ് പാനൽ വീഡിയോ +search ഒരു നമ്പർ നൽകുക. തിരഞ്ഞെടുത്തു '' എന്നതിനുശേഷം ഒരു ഭാഗം നൽകുക.'' അപൂർണ്ണമാണ്. +'പ്രോഗ്രസ്' ഇൻഡിക്കേറ്റർ പട്ടിക മാര്‍ക്കര്‍ അടുത്ത മാസം കാണിക്കുക +വരി ശീർഷകം മൂല്യം എന്നതില്‍ കൂടുതലോ സമമോ ആയിരിക്കണം. '' എന്നതിനുശേഷം ഒരു ഭാഗം നൽകുക.'' അപൂർണ്ണമാണ്. +സംരക്ഷിക്കുക ആകാരം +ട്രീ ഇനം ഒന്നോ അതില്‍ക്കൂടുതലോ ഫയലുകള്‍ ദയവായി തിരഞ്ഞെടുക്കുക. പൂർണ്ണ സ്‌ക്രീനിൽ നിന്ന് പുറത്തുകടക്കുക അണ്‍ചെക്ക് ചെയ്യുക +തീയതി പിക്കർ ശബ്‌ദമുള്ളതാക്കുക +സമയം പ്ലേബാക്ക് താൽക്കാലികമായി നിർത്തുക +ബട്ടൺ മറ്റുള്ളവ... വീണ്ടും സജ്ജീകരിക്കുക 2048 (High Grade) ആഴ്‌ച -വിലാസം +സ്‌പിൻ ബട്ടൺ വര്‍ഷം +ലിസ്റ്റ് ബോക്‌സ് ഓഡിയോ ട്രാക്ക് ശബ്‌ദമുള്ളതാക്കുക ആക്റ്റിവേറ്റ് ചെയ്യുക +മീറ്റർ മൂവി ടൈംലൈൻ തമ്പ് മൂവി ടൈം സ്‌ക്രബ്ബർ തമ്പ് മാസം തിരഞ്ഞെടുക്കുന്ന പാനൽ കാണിക്കുക ഓഡിയോ സമയ സ്‌ക്രബർ +സമയ പിക്കർ പൂർണ്ണ സ്‌ക്രീനിലേക്ക് പോവുക +ലോഗ് മായ്‌ക്കുക സമീപകാല തിരയലുകള്‍ മറ്റുള്ളവ... സ്റ്റെപ്പർ +റൂളർ +ടൂൾടിപ്പ് പ്ലഗിൻ ലോഡുചെയ്യാനായില്ല. നിശബ്‌ദമാക്കുക അമര്‍ത്തുക അടച്ച അടിക്കുറിപ്പുകൾ മറയ്‌ക്കുക +ഡയറക്‌ടറി +ബ്ലോക്ക്‌കോട്ട് മൂവി ടൈം അഭ്യര്‍ത്ഥി ച്ചഫോര്‍മാറ്റ് ദയവായി പൊരുത്തപ്പെടുത്തുക. കഴിഞ്ഞ സമയം പട്ടികയിലെ ഒരു ഇനം ദയവായി തിരഞ്ഞെടുക്കുക +കോളം പേര് ഫയല്‍ തിരഞ്ഞെടുക്കൂ മറ്റുള്ളവ... +ടാബ് മണിക്കൂര്‍‌ അവശേഷിക്കുന്ന സമയം അടച്ച അടിക്കുറിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നത് നിർത്തുക HTML ഉള്ളടക്കം താല്‍‌ക്കാലികമായി നിര്‍‌ത്തുക +ഡിസ്‌ക്ലോഷർ ത്രികോണം ഇമെയിൽ വിലാസത്തിൽ '' ഉൾപ്പെടുത്തുക. '' എന്നതിൽ ഒരു '' കാണുന്നില്ല. +സ്‌പ്ലിറ്റർ ഒരു ഫയലും തിരഞ്ഞെടുത്തിട്ടില്ല തിരയൽ ടെക്‌സ്റ്റ് ഫീൽഡ് അടച്ച അടിക്കുറിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നത് ആരംഭിക്കുക ഈ ആഴ്‌ച -ഇത് തിരയാവുന്ന സൂചികയാണ്. തിരയല്‍ കീവേഡുകള്‍ നല്‍കുക: +ഇത് തിരയാവുന്ന സൂചികയാണ്. തിരയൽ കീവേഡുകള്‍ നല്‍കുക: പൂർണ്ണ സ്‌ക്രീൻ മോഡിൽ മൂവി പ്ലേ ചെയ്യുക വർഷം ഇമേജ് മാപ്പ് അസാധുവായ മൂല്യം. പരിശോധിക്കൂ +ടൂൾബാർ ഫയലുകള്‍ ദയവായി ഒരു ഫയല്‍ തരം തിരഞ്ഞെടുക്കുക. +റേഡിയോ ബട്ടൺ +ഡ്രോപ്പ് ഡൗൺ ബട്ടൺ മിനിറ്റ് മീഡിയ നിയന്ത്രണം നിർവചന ലിസ്റ്റ് @@ -127,10 +178,15 @@ പ്ലേബാക്ക് ആരംഭിക്കുക main മാസം +തുറക്കുക നിലവിലെ സമയം നിമിഷങ്ങളിൽ നാവിഗേഷൻ അടച്ച അടിക്കുറിപ്പുകൾ കാണിക്കുക +പ്രമാണം മാത്ത് മറ്റുള്ളവ... +അലേർട്ട് , -ന് ആരംഭിക്കുന്നു +സെൽ +മെനു \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_mr.xtb b/chromium/content/app/strings/translations/content_strings_mr.xtb index fc986546d67..1ce9f37c917 100644 --- a/chromium/content/app/strings/translations/content_strings_mr.xtb +++ b/chromium/content/app/strings/translations/content_strings_mr.xtb @@ -2,22 +2,30 @@ रिक्त +मेनू आयटम ऑडिओ ट्रॅक निःशब्द करा टॉगल बटण सेकंद कृपया एक ईमेल पत्ता प्रविष्ट करा. पूरक +marquee +स्लायडर अलीकडील शोध साफ करा +संवाद लेख बॅनर प्रदेश या महिन्यात फायली निवडा +व्यस्त सूचक कृपया एक रिक्त-नसलेला ईमेल पत्ता प्रविष्ट करा. अन्य... +सूचना_संवाद +ट्री ग्रीड AM/PM दूरस्थ डिव्हाइसवर प्ले करा निवडा +बाह्यरेखा dd स्थिती मूल्य किंवा आधीचे असणे आवश्यक आहे. @@ -26,8 +34,10 @@ कृपया हा मजकूर वर्ण लहान किंवा कमी करा (आपण सध्या वर्ण वापरत आहात). आठवडा , चेकबॉक्‍स +तारीख आणि वेळ निवडक दिवस कृपया हे फील्ड भरा. +टॅब सूची अलीकडील शोध नाहीत प्ले करा कृपया एक वैध मूल्य प्रविष्ट करा. जवळील वैध मूल्य आहे. @@ -35,16 +45,25 @@ अधोलेख ऑडिओ मूल्य किंवा नंतरचे असणे आवश्यक आहे. +पॉप अप बटण कृपया आपण पुढे चालू ठेवू इच्छित असल्यास हा बॉक्स पहा. 1024 (मध्यम प्रत) +रेडिओ गट वर्तमान चित्रपट स्थिती कृपया ईमेल पत्त्यांची स्वल्पविरामाद्वारे विभक्त सूची प्रविष्ट करा. +क्लिक करा +ऑब्जेक्ट हायलाइट केलेली सामग्री दुवा कृपया एक वैध मूल्य प्रविष्ट करा. फील्ड अपूर्ण आहे किंवा अवैध तारीख आहे. +सारणी +टीप कृपया एखादी URL प्रविष्ट करा. +टायमर +काँबो बॉक्स मूल्य पेक्षा कमी किंवा समान असावे. परिभाषा +शीर्षक चित्रपटाचे शिल्लक सेकंद स्विच करा mm @@ -52,60 +71,89 @@ संज्ञा कृपया हा मजकूर वर्ण किंवा त्यापेक्षा अधिक मोठा करा (आपण सध्‍या वर्ण वापरत आहात). '' मागुन येणार्‍या भागामध्ये '' चिन्ह नसावे. +अनुप्रयोग +स्क्रोल बार मिलिसेकंद +ग्राफिक मागील महिना दर्शवा जंप करा +रंग निवडक +मेनू बार +सामग्री माहिती कृपया एक वैध मूल्य प्रविष्ट करा. दोन जवळील वैध मूल्ये आणि आहेत. +मेनू बटण तपशील फॉर्म +tree '' चे अनुसरण करणार्‍या भागामध्ये '' चिन्ह नसावे. +टॅब पॅनेल व्हिडिओ +search कृपया एक नंबर प्रविष्ट करा. निवडले कृपया '' चे अनुसरण करणारा भाग प्रविष्ट करा. '' अपूर्ण आहे. +प्रगती सूचक सूची चिन्हक पुढील महिना दर्शवा +पंक्ती शीर्षलेख मूल्य पेक्षा मोठे किंवा समान असावे. कृपया '' मागुन येणारा भाग प्रविष्ट करा. '' अपूर्ण आहे. +जतन करा आकृती +ट्री आयटम कृपया एक किंवा अधिक फायली निवडा. पूर्ण स्क्रीनमधून निर्गमन करा अनचेक +तारीख निवडक सशब्द करा +time प्लेबॅकला विराम द्या +बटण अन्य... रीसेट करा 2048 (उच्च ग्रेड) आठवडा -पत्ता +स्पिन बटण वर्ष +सूची बॉक्स ऑडिओ ट्रॅक सशब्द करा सक्रिय करा +मीटर चित्रपट टाइमलाइन थंब चित्रपट वेळ स्क्रबर थंब महिना निवड पॅनेल दर्शवा ऑडिओ वेळ स्क्रबर +वेळ निवडक पूर्ण स्क्रीन प्रविष्ट करा +लॉग साफ करा अलीकडील शोध अन्य... stepper +मोजपट्टी +टूलटिप प्लगिन लोड करणे शक्य झाले नाही. नि:शब्द करा दाबा बंद मथळा लपवा +निर्देशिका +ब्लॉककोट चित्रपटाची वेळ कृपया विनंती केलेले स्वरूपन जुळवा. लोटलेला अवधी कृपया सूचीमधील आयटम निवडा. +स्तंभ शीर्षलेख फाइल निवडा अन्य... +टॅब तास शिल्लक वेळ बंद मथळा प्रदर्शित करणे थांबवा HTML सामुग्री विराम द्या +त्रिकोण प्रकटन कृपया ईमेल पत्त्यामध्ये '' समाविष्ट करा. '' '' गमावत आहे. +विभाजक कोणतीही फाइल निवडलेली नाही मजकूर फील्ड दर्शवा बंद मथळा प्रदर्शित करणे प्रारंभ करा @@ -116,8 +164,11 @@ प्रतिमा नकाशा अवैध मूल्य. तपासा +टूलबार फायली कृपया एखादी फाइल निवडा. +रेडिओ बटण +ड्रॉप डाउन बटण मिनिटे माध्यम नियंत्रण परिभाषा सूची @@ -127,10 +178,15 @@ प्लेबॅक आरंभ करा मुख्य महिना +उघडा वर्तमान वेळ सेकंदांमध्ये नेव्हिगेशन बंद मथळा दर्शवा +दस्तऐवज गणित अन्य... +सूचना रोजी प्रारंभ होणारा, +सेल +मेनू \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ms.xtb b/chromium/content/app/strings/translations/content_strings_ms.xtb index 03f0cac4110..53f551a6d9b 100644 --- a/chromium/content/app/strings/translations/content_strings_ms.xtb +++ b/chromium/content/app/strings/translations/content_strings_ms.xtb @@ -2,22 +2,30 @@ kosong +item menu redamkan runut audio butang togol Saat Sila masukkan alamat e-mel. pelengkap +marki +peluncur Kosongkan Carian Baru-baru Ini +dialog artikel sepanduk wilayah Bulan ini Pilih Fail +penunjuk sibuk Sila masukkan alamat e-mel bukan kosong. Lain-lain... +dialog_makluman +grid pohon PG / P/M main pada peranti jauh pilih +garis bentuk dd status Nilai mestilah atau lebih awal. @@ -26,8 +34,10 @@ Sila pendekkan teks ini menjadi aksara atau kurang (anda kini menggunakan aksara). Minggu , kotak pilihan +pemilih tarikh dan masa Hari Sila isikan medan ini. +senarai tab Tiada carian baru-baru ini main Sila masukkan nilai yang sah. Nilai sah yang terdekat ialah . @@ -35,16 +45,25 @@ pembawah audio Nilai mestilah atau kemudian. +butang timbul Sila tandakan kotak ini jika anda mahu teruskan. 1024 (Gred Sederhana) +kumpulan radio status filem semasa Sila masukkan senarai alamat e-mel yang dipisahkan dengan koma. +klik +objek kandungan yang diserlahkan pautan Sila masukkan nilai yang sah. Medan tidak lengkap atau mengandungi tarikh yang tidak sah. +jadual +nota Sila masukkan URL. +pemasa +kotak kombo Nilai mestilah kurang daripada atau sama dengan . takrif +pengepala bilangan saat filem yang tinggal tukar mm @@ -52,60 +71,89 @@ istilah Sila panjangkan teks ini kepada aksara atau lebih (anda sedang menggunakan aksara). Bahagian yang diikuti oleh '' tidak boleh mengandungi simbol ''. +aplikasi +bar tatal Milisaat +grafik Tunjukkan bulan sebelumnya lompat +pemilih warna +bar menu +maklumat kandungan Sila masukkan nilai yang sah. Dua nilai sah yang terdekat ialah dan . +butang menu Butiran borang +pohon Bahagian selepas '' tidak boleh mengandungi simbol ''. +panel tab video +search Sila masukkan nombor. dipilih Sila masukkan bahagian selepas ''. '' tidak lengkap. +penunjuk kemajuan penanda senarai Tunjukkan bulan seterusnya +pengepala baris Nilai mesti lebih besar daripada atau sama dengan . Sila masukkan bahagian diikuti oleh ''. '' tidak lengkap. +Simpan angka +item pohon Sila pilih satu fail atau lebih. keluar daripada skrin penuh nyahpilih +pemilih tarikh nyahredam +masa jeda main balik +butang Lain-lain... Tetapkan semula 2048 (Gred Tinggi) Minggu -alamat +butang putar Tahun +kotak senarai nyahredam runut audio aktifkan +meter ibu jari garis masa filem ibu jari pembersih masa filem Tunjukkan panel pilihan bulan pembersih masa audio +pemilih masa memasuki skrin penuh +log Kosongkan Carian Baru-baru Ini Lain-lain... pelangkah +pembaris +tip alat Tidak dapat memuatkan pemalam. redam tekan sembunyikan kapsyen tertutup +direktori +petikan blok masa filem Sila padankan dengan format yang diminta. masa berlalu Sila pilih item dalam senarai. +pengepala lajur Pilih Fail Lain-lain... +tab Jam masa yang tinggal berhenti memaparkan kapsyen tertutup Kandungan HTML jeda +segi tiga pendedahan Sila masukkan '' dalam alamat e-mel. '' tiada ''. +pemisah Tiada fail dipilih medan teks carian mula memaparkan kapsyen tertutup @@ -116,8 +164,11 @@ peta imej Nilai tidak sah. periksa +bar alat fail Sila pilih fail. +butang radio +butang lungsur Minit kawalan media senarai takrif @@ -127,10 +178,15 @@ mulakan main balik utama Bulan +buka masa semasa dalam saat navigasi paparkan kapsyen tertutup +dokumen matematik Lain-lain... +makluman , bermula pada +sel +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_nl.xtb b/chromium/content/app/strings/translations/content_strings_nl.xtb index 405e94a5ec5..552abf6e840 100644 --- a/chromium/content/app/strings/translations/content_strings_nl.xtb +++ b/chromium/content/app/strings/translations/content_strings_nl.xtb @@ -2,22 +2,30 @@ leeg +menu-item audiotrack dempen schakelknop Seconden Geef een e-mailadres op. aanvullend +marquee +schuifregelaar Recente zoekopdrachten wissen +dialoogvenster artikel banner regio Deze maand Bestanden kiezen +bezetindicator Geef een e-mailadres op. Anders... +dialoogvenster voor meldingen +boomstructuur a.m./p.m. afspelen op extern apparaat Selecteren +overzicht dd status Waarde moet of eerder zijn. @@ -26,8 +34,10 @@ Kort deze tekst in tot tekens of minder (je gebruikt momenteel tekens). Week , selectievakje +datum- en tijdkiezer Dag Vul dit veld in. +tabbladlijst Geen recente zoekopdrachten afspelen Geef een geldige waarde op. De dichtstbijzijnde geldige waarde is . @@ -35,16 +45,25 @@ voettekst audio Waarde moet of later zijn. +pop-upknop Vink dit selectievakje aan als je wilt doorgaan. 1024 (gemiddeld niveau) +groep met keuzerondjes huidige filmstatus Voer een door komma's gescheiden lijst met e-mailadressen in. +klikken +object gemarkeerde inhoud link Voer een geldige waarde in. Het veld is onvolledig of bevat een ongeldige datum. +tabel +opmerking Geef een URL op. +timer +keuzelijst met invoervak Waarde moet kleiner dan of gelijk zijn aan . definitie +koptekst aantal seconden van resterende film schakelaar mm @@ -52,60 +71,89 @@ term Breid deze tekst uit tot tekens of meer (je gebruikt momenteel tekens). Het naamgedeelte vóór '' mag niet het teken '' bevatten. +app +schuifbalk Milliseconden +afbeelding Vorige maand weergeven Gaan naar +kleurkiezer +menubalk +informatie over content Geef een geldige waarde op. De twee dichtstbijzijnde geldige waarden zijn en . +menuknop Details formulier +structuur Het adresgedeelte na '' mag niet het teken '' bevatten. +deelvenster met tabblad video +zoeken Voer een getal in. geselecteerd Geef een adresgedeelte op na ''. '' is onvolledig. +voortgangsindicator lijstmarkering Volgende maand weergeven +rijkop Waarde moet groter dan of gelijk zijn aan . Geef een naamgedeelte op, gevolgd door ''. '' is onvolledig. +Opslaan figuur +item in boomstructuur Selecteer een of meer bestanden. volledig scherm sluiten Deselecteren +datumkiezer dempen opheffen +tijd afspelen onderbreken +knop Anders... Terugzetten 2048 (hoog niveau) Week -adres +kringveld Jaar +keuzelijst dempen van audiotrack opheffen Activeren +meter miniatuur van tijdlijn van film miniatuur van schuifbalk met tijd van de film Deelvenster voor maandselectie weergeven schuifbalk met audiotijd +tijdkiezer volledig scherm openen +logboek Wissen Recente zoekopdrachten Anders... stappenregelaar +liniaal +knopinfo Kan plug-in niet laden. dempen Indrukken ondertiteling verbergen +directory +blok met geciteerde tekst tijd van de film Zorg dat de indeling voldoet aan de gevraagde indeling. verstreken tijd Selecteer een item in de lijst. +kolomkop Bestand kiezen Anders... +tab Uur resterende tijd het weergeven van ondertiteling stoppen HTML-inhoud onderbreken +driehoek voor samen-/uitvouwen Gebruik een '' in thet e-mailadres. In '' ontbreekt een ''. +splitser Geen bestand gekozen zoektekstveld het weergeven van ondertiteling starten @@ -116,8 +164,11 @@ image map Ongeldige waarde. Selecteren +werkbalk bestanden Selecteer een bestand. +keuzerondje +dropdown-knop Minuten mediacontrole definitielijst @@ -127,10 +178,15 @@ afspelen starten hoofd Maand +openen huidige tijd in seconden navigatie ondertiteling weergeven +document wiskunde Anders... +melding , die begint op +cel +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_no.xtb b/chromium/content/app/strings/translations/content_strings_no.xtb index da055713bed..4265becbb5b 100644 --- a/chromium/content/app/strings/translations/content_strings_no.xtb +++ b/chromium/content/app/strings/translations/content_strings_no.xtb @@ -2,22 +2,30 @@ tom +menyelement slå av lydsporet av/på-knapp Sekunder Skriv inn en e-postadresse. komplementær +marquee +glidebryter Fjern nylige søk +dialog article banner område Denne måneden Velg filer +opptatt-indikator Skriv inn en ikke-tom e-postadresse. Andre +varseldialog +trerutenett AM/PM spill på ekstern enhet velg +omriss dd status Verdien må være eller før. @@ -26,8 +34,10 @@ Forkort denne teksten til tegn eller færre (for øyeblikket bruker du tegn). Uke , avmerkingsboks +dato- og klokkeslettvelger Dag Vennligst fyll ut dette feltet. +faneliste Ingen nylige søk spill av Skriv inn en gyldig verdi. Den nærmeste, gyldige verdien er . @@ -35,16 +45,25 @@ fotnote lyd Verdien må være eller senere. +forgrunnsknapp Kryss av denne boksen hvis du vil fortsette. 1024 (middels) +gruppe med alternativknapper gjeldende filmstatus Skriv inn en liste over e-postadresser atskilt med komma. +klikk +objekt merket innhold link Skriv inn en gyldig verdi. Feltet er ufullstendig, eller har en ugyldig dato. +tabell +note Skriv inn en nettadresse. +tidtaker +kombinasjonsfelt Verdien må være mindre enn eller lik . definisjon +overskrift antall sekunder som gjenstår av filmen bryter mm @@ -52,60 +71,89 @@ term Du må forlenge denne teksten til tegn eller mer (for øyeblikket bruker du tegn). En del etterfulgt av «» kan ikke inneholde symbolet «». +program +rullefelt Millisekunder +grafikk Se forrige måned hopp +fargevelger +menyrad +innholdsinformasjon Skriv inn en gyldig verdi. De to nærmeste, gyldige verdiene er og . +menyknapp Detaljer skjema +tree En del etterfulgt av «» kan ikke inneholde symbolet «». +fanepanel video +søk Skriv inn et tall. er valgt Skriv inn en del etterfulgt av «». «» er ufullstendig. +fremdriftsindikator listemarkør Se neste måned +radoverskrift Verdien må være større enn eller lik . Skriv inn en del etterfulgt av «». «» er ufullstendig. +Lagre figur +treelement Velg én eller flere filer. avslutt fullskjerm fjern merke +datovelger slå på lyden +time stopp avspillingen midlertidig +knapp Andre Tilbakestill 2048 (sterk) Uke -adresse +verdisettingsknapp År +listefelt slå på lydsporet aktiver +måler filmtidslinjemarkøren markøren for filmtidslinjen Se panelet for valg av måned lydtidslinje +klokkeslettvelger gå til fullskjermmodus +logg Tøm Nylige søk Annen stepper +linjal +verktøytips Kunne ikke laste inn programtillegget. kutt lyd trykk skjul teksting +katalog +blokksitat gå til et annet tidspunkt Sørg for samsvar med det forespurte formatet. brukt tid Velg en artikkel i listen. +kolonneoverskrift Velg fil Andre +tab Timer tid som gjenstår slå av tekstingen HTML-innhold stans midlertidig +trekant for ekstra innhold Inkluder en «» i e-postadressen. «» mangler en «». +vindusdeler Ingen fil valgt søketekstfelt slå på tekstingen @@ -116,8 +164,11 @@ bildekart Ugyldig verdi merk av +verktøyrad filer Velg en fil. +alternativknapp +rullegardinknapp Minutter mediekontroll definisjonsliste @@ -127,10 +178,15 @@ start avspillingen main Måned +åpne gjeldende tid i sekunder navigasjon vis teksting +dokument math Andre +varsel , med start +celle +meny \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pl.xtb b/chromium/content/app/strings/translations/content_strings_pl.xtb index 26543259dd5..6277f53a751 100644 --- a/chromium/content/app/strings/translations/content_strings_pl.xtb +++ b/chromium/content/app/strings/translations/content_strings_pl.xtb @@ -2,22 +2,30 @@ puste +element menu wycisz ścieżkę dźwiękową przycisk przełączania Sekundy Wprowadź adres e-mail. pomocniczy +marquee +suwak Wyczyść ostatnie wyszukiwania +okno dialogowe article baner region W tym miesiącu Wybierz pliki +wskaźnik zajętości Podaj adres e-mail. Inny... +alert_dialog +siatka drzewa rano/po południu odtwarzanie na urządzeniu zdalnym wybierz +schemat dd stan Musisz podać wartość lub wcześniejszą. @@ -26,8 +34,10 @@ Skróć ten tekst do maksymalnie znaków (w tej chwili korzystasz z znaków). Tydzień , pole wyboru +selektor daty i godziny Dzień Wypełnij to pole. +lista kart Brak ostatnich wyszukiwań odtwórz Podaj prawidłową wartość. Najbliższa prawidłowa wartość to . @@ -35,16 +45,25 @@ stopka dźwięk Musisz podać wartość lub późniejszą. +przycisk wyskakującego okienka Zaznacz to pole, jeśli chcesz kontynuować. 1024 (średni poziom) +grupa przycisków opcji bieżący stan filmu Podaj listę adresów e-mail rozdzielonych przecinkami. +kliknij +obiekt wyróżniona treść link Wpisz prawidłową wartość. Pole jest niekompletne lub zawiera nieprawidłową datę. +tabela +note Wprowadź adres URL. +licznik czasu +pole złożone Wartość nie może być większa niż . definicja +nagłówek pozostały czas filmu w sekundach przełącznik mm @@ -52,60 +71,89 @@ termin Wydłuż ten tekst przynajmniej do znaków (teraz używasz znaków). Część przed znakiem „” nie może zawierać symbolu „”. +aplikacja +pasek przewijania Milisekundy +grafika Pokaż poprzedni miesiąc przejdź +selektor kolorów +pasek menu +informacje o zawartości Podaj prawidłową wartość. Dwie najbliższe prawidłowe wartości to i . +przycisk menu Szczegóły formularz +tree Część po znaku „” nie może zawierać symbolu „”. +panel karty film +search Wpisz liczbę. Wybrano: Podaj część po znaku „”. Adres „” jest niepełny. +wskaźnik postępu znacznik listy Pokaż przyszły miesiąc +nagłówek wiersza Wartość nie może być mniejsza niż . Podaj część przed znakiem „”. Adres „” jest niepełny. +Zapisz figura +element drzewa Wybierz jeden lub kilka plików. zamknij pełny ekran odznacz +selektor daty wyłącz wyciszenie +time wstrzymaj odtwarzanie +przycisk Inny... Resetuj 2048 (wysoki poziom) Tydzień -adres +przycisk przewijany Rok +pole listy wyłącz wyciszenie ścieżki dźwiękowej aktywuj +miernik miniatura osi czasu filmu miniatura paska czasu odtwarzania filmu Pokaż panel wyboru miesiąca pasek czasu odtwarzania dźwięku +selektor godziny przejdź do pełnego ekranu +dziennik Wyczyść Ostatnie wyszukiwania Inny... element kroczący +linijka +etykietka Nie można załadować wtyczki. wyciszenie naciśnij ukryj napisy +katalog +cytat blokowy czas filmu Podaj wartość w wymaganym formacie. upłynęło czasu Wybierz element z listy. +nagłówek kolumny Wybierz plik Inny... +tab Godziny pozostały czas przestań pokazywać napisy Treść HTML wstrzymaj +trójkąt rozwinięcia Uwzględnij znak „” w adresie e-mail. W adresie „” brakuje znaku „”. +podział Nie wybrano pliku pole tekstowe wyszukiwania zacznij pokazywać napisy @@ -116,8 +164,11 @@ mapa grafiki Nieprawidłowa wartość. zaznacz +pasek narzędzi Liczba plików: Wybierz plik. +przycisk opcji +przycisk listy Minuty sterowanie multimediami lista definicji @@ -127,10 +178,15 @@ rozpocznij odtwarzanie główny Miesiąc +otwórz bieżący czas w sekundach nawigacja pokaż napisy +dokument matematyczny Inny... +alert , początek +komórka +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pt-BR.xtb b/chromium/content/app/strings/translations/content_strings_pt-BR.xtb index 18f9742862b..99642d624d3 100644 --- a/chromium/content/app/strings/translations/content_strings_pt-BR.xtb +++ b/chromium/content/app/strings/translations/content_strings_pt-BR.xtb @@ -2,22 +2,30 @@ em branco +item de menu desativar som da faixa de áudio botão "alternar" Segundos Insira um endereço de e-mail. complementar +marquee +controle deslizante Limpar pesquisas recentes +caixa de diálogo article banner região Este mês Escolher arquivos +indicador de atividade Insira um endereço de e-mail que não esteja vazio. Outras... +alert_dialog +grade de árvore AM/PM reproduzir em dispositivo remoto selecione +contorno dd status O valor deve ser ou anterior. @@ -26,8 +34,10 @@ Reduza este texto para caracteres ou menos (você está usando caracteres). Semana , caixa de seleção +seletor de data e hora Dia Preencha este campo. +lista de guias Nenhuma pesquisa recente reproduzir Insira um valor válido. O valor válido mais próximo é . @@ -35,16 +45,25 @@ rodapé áudio O valor deve ser ou posterior. +botão pop-up Marque esta caixa se deseja continuar. 1024 (Nível médio) +grupo de rádio status atual do filme Insira uma lista de endereços de e-mail separados por vírgula. +clicar +objeto conteúdo destacado link Insira um valor válido. O campo está incompleto ou tem uma data inválida. +tabela +note Insira um URL. +temporizador +caixa de combinação O valor deve ser menor ou igual a . definição +cabeçalho segundos restantes do filme alternar mm @@ -52,60 +71,89 @@ termo Aumente este texto para caracteres ou mais. No momento, você está usando caracteres). Uma parte seguida por "" não deve conter o símbolo "". +app +barra de rolagem Milésimos de segundo +gráfico Mostrar mês anterior pular +Seletor de cores +barra de menu +informações sobre o conteúdo Insira um valor válido. Os dois valores válidos mais próximos são e . +botão de menu Detalhes formulário +tree A parte depois de "" não deve conter o símbolo "". +painel da guia vídeo +pesquisar Insira um número. selecionados Insira uma parte depois de "". "" está incompleto. +indicador de progresso marcador de lista Mostrar próximo mês +cabeçalho de linha O valor deve ser maior ou igual a . Insira uma parte seguida por "". "" está incompleto. +Salvar figura +item de árvore Selecione um ou mais arquivos. sair do modo tela cheia desmarcar +seletor de data ativar som +time pausar reprodução +botão Outras... Redefinir 2048 (Nível alto) Semana -endereço +botão de rotação Ano +caixa de listagem ativar som da faixa de áudio ativar +medidor miniatura da barra de progressão do filme miniatura da barra de progressão do filme Mostrar painel de seleção de mês barra de progressão do áudio +seletor de hora entrar no modo tela cheia +log Limpar Pesquisas recentes Outro... stepper +régua +dica Não foi possível carregar o plug-in. sem som pressione ocultar legendas ocultas +diretório +bloco de texto tempo de filme É preciso que o formato corresponda ao exigido. tempo decorrido Selecione um item da lista. +cabeçalho da coluna Escolher arquivo Outras... +tab Horas tempo restante parar de exibir legendas ocultas Conteúdo HTML pausar +triângulo de divulgação Inclua um "" no endereço de e-mail. "" está com um "" faltando. +divisor Nenhum arquivo selecionado campo de texto da pesquisa começar a exibir legendas ocultas @@ -116,8 +164,11 @@ mapa de imagens Valor inválido. marcar +barra de ferramentas arquivos Selecione um arquivo. +botão de opção +botão suspenso Minutos controle de mídia lista de definições @@ -127,10 +178,15 @@ iniciar reprodução main Mês +abrir tempo atual em segundos navegação mostrar legendas ocultas +documento matemática Outras... +alerta , que começa em +célula +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pt-PT.xtb b/chromium/content/app/strings/translations/content_strings_pt-PT.xtb index a0e5e1fb734..2398bd250e7 100644 --- a/chromium/content/app/strings/translations/content_strings_pt-PT.xtb +++ b/chromium/content/app/strings/translations/content_strings_pt-PT.xtb @@ -2,22 +2,30 @@ em branco +item de menu desativar a faixa de áudio botão alternar Segundos Introduza um endereço de email. complementar +painel rolante +controlo de deslize Limpar pesquisas recentes +caixa de diálogo article banner região Este mês Escolher Ficheiros +indicador de ocupado Introduza um endereço de email que não esteja vazio. Outra... +alert_dialog +grelha de árvore AM/PM reproduzir no dispositivo remoto seleccionar +contorno dd estado O valor tem de ser ou anterior. @@ -26,8 +34,10 @@ Encurte este texto para caracteres ou menos (está atualmente a utilizar caracteres). Semana , de caixa de verificação +selecionador de data e hora Dia Preencha este campo. +lista de separadores Nenhuma pesquisa recente reproduzir Introduza um valor válido. O valor válido mais próximo é . @@ -35,16 +45,25 @@ rodapé áudio O valor tem de ser ou posterior. +botão pop-up Seleccione esta caixa se pretender continuar. 1024 (Tamanho médio) +grupo de botões de opção estado atual do filme Introduza uma lista de endereços de email separados por vírgula. +clicar +objeto conteúdo realçado link Introduza um valor válido. O campo está incompleto ou tem uma data inválida. +tabela +note Introduza um URL. +temporizador +caixa de combinação O valor tem de ser inferior ou igual a . definição +título segundos que restam ao filme alternar mm @@ -52,60 +71,89 @@ termo Aumente este texto para carateres ou mais (está atualmente a utilizar carateres). Uma parte seguida de "" não deve conter o símbolo "". +aplicação +barra de deslocamento Milissegundos +gráfico Mostrar mês anterior ir para +seletor de cores +barra de menu +informações do conteúdo Introduza um valor válido. Os dois valores válidos mais próximos são e . +botão de menu Detalhes formulário +árvore Uma parte a seguir a "" não deve conter o símbolo "". +painel de separadores vídeo +search Introduza um número. selecionados Introduza uma parte a seguir a "". "" está incompleto. +indicador de progresso marcador de lista Mostrar mês seguinte +cabeçalho da linha O valor tem de ser superior ou igual a . Introduza uma parte seguida de "". "" está incompleto. +Guardar figura +item de árvore Seleccione um ou mais ficheiros. sair do modo de ecrã inteiro desmarcar +selecionador de data reativar som +hora interromper a reprodução +botão Outra... Repor 2048 (Tamanho grande) Semana -endereço +botão giratório Ano +caixa de lista reativar a faixa de áudio activar +contador botão da cronologia do filme botão do scrubber do tempo do filme Mostrar painel de seleção do mês controlo de arrasto do tempo do áudio +selecionador de hora entrar no modo de ecrã inteiro +log Limpar Pesquisas recentes Outros... stepper +régua +sugestão Não foi possível carregar o plug-in. desativar som premir esconder legendas ocultas +diretório +blockquote tempo do filme Faça corresponder o formato pedido. tempo decorrido Seleccione um item na lista. +cabeçalho da coluna Escolher ficheiro Outra... +tab Horas tempo restante parar de apresentar legendas ocultas Conteúdo HTML pausa +triângulo de divulgação Inclua um "" no endereço de email. Falta um "" em "". +divisor Nenhum ficheiro selecionado campo de texto de pesquisa apresentar legendas ocultas @@ -116,8 +164,11 @@ mapa de imagem Valor inválido. verificar +barra de ferramentas ficheiros Seleccione um ficheiro. +botão de opção +botão pendente Minutos controlo multimédia lista de definições @@ -127,10 +178,15 @@ iniciar reprodução main Mês +abrir tempo atual em segundos navegação apresentar legendas ocultas +documento math Outra... +alerta , a partir de +célula +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ro.xtb b/chromium/content/app/strings/translations/content_strings_ro.xtb index 8c60cf84b02..af8dbed9a30 100644 --- a/chromium/content/app/strings/translations/content_strings_ro.xtb +++ b/chromium/content/app/strings/translations/content_strings_ro.xtb @@ -2,22 +2,30 @@ necompletat +element de meniu dezactivați sunetul înregistrării audio buton de comutare Secunde Introdu o adresă de e-mail. complementar +text derulant +glisor Șterge căutările recente +dialog articol banner regiune Luna aceasta Alege fișierele +indicator de procesare Introduceți o adresă de e-mail concretă. Altele... +dialog de alertă +grilă arbore AM/PM redă pe dispozitiv la distanță selectează +schemă zz stare Valoarea pentru dată/oră trebuie să fie sau una anterioară. @@ -26,8 +34,10 @@ Micșorează acest text la cel mult (de) caractere (în prezent utilizezi (de) caractere). Săptămâna , casetă de selectare +selector pentru dată și oră Zi Completează acest câmp. +listă de file Nicio căutare recentă redați Introduceți o valoare validă. Cea mai apropiată valoare validă este . @@ -35,16 +45,25 @@ notă de subsol audio Valoarea pentru dată/oră trebuie să fie sau una ulterioară. +buton pop-up Bifeză caseta dacă vrei să continui. 1024 (nivel mediu) +grup butoane radio starea actuală a filmului Introdu o listă de adrese de e-mail separate prin virgulă. +dă clic +obiect conținut evidențiat link Introduceți o valoare validă. Câmpul este incomplet sau conține o dată nevalidă. +tabel +notă Introdu o adresă URL. +temporizator +casetă combo Valoarea trebuie să fie mai mică sau egală cu . definiție +titlu numărul secundelor rămase din film comutator ll @@ -52,60 +71,89 @@ termen Mărește acest text la cel puțin (de) caractere (în prezent folosești (de) caractere). Valoarea urmată de semnul „” nu trebuie să conțină simbolul „”. +aplicație +bară de derulare Milisecunde +element grafic Afișează luna anterioară accesează +selector de culoare +bară de meniu +informații privind conținutul Introduceți o valoare validă. Cele mai apropiate valori valide sunt și . +buton de meniu Detalii formular +arbore Valoarea care urmează după semnul „” nu trebuie să conțină simbolul „”. +panou de file video +căutați Introduceți un număr. selectate Introduceți o valoare după semnul „”. Adresa „” nu este completă. +indicator de progres marcator de listă Afișează luna următoare +antet rând Valoarea trebuie să fie mai mare sau egală cu . Introduceți o valoare urmată de semnul „”. Adresa „” nu este completă. +Salvează cifră +element arbore Selectează unul sau mai multe fișiere. ieșiți din ecranul complet debifează +selector dată activați sunetul +oră întrerupeți redarea +buton Altele... Resetează 2048 (nivel ridicat) Săptămână -adresă +buton incrementare/decrementare An +casetă listă activați sunetul înregistrării audio activează +instrument de măsurare miniatură cronologie film miniatură glisor redare film Afișează panoul de selectare a lunii glisor redare audio +selector oră deschideți în ecran complet +jurnal Ștergeți Căutări recente Altele... control numeric cu incrementare/decrementare +riglă +balon explicativ Nu s-a putut încărca pluginul. dezactivați sunetul apasă ascundeți subtitrările +director +blockquote durată film Respectă formatul solicitat. timp scurs Selectează un articol din listă. +antet coloană Alege fișierul Altele... +filă Ore timp rămas nu mai afișați subtitrările Conținut HTML întrerupe +triunghi pentru afișare Includeți semnul „” în adresa de e-mail. Din adresa „” lipsește semnul „”. +element de divizare Nu ai ales niciun fișier câmp pentru căutarea textului începeți să afișați subtitrările @@ -116,8 +164,11 @@ hartă imagine Valoare nevalidă. bifează +bară de instrumente (de) fișiere Selectează un fișier. +buton radio +buton drop-down Minute comandă media listă de definiții @@ -127,10 +178,15 @@ începeți redarea principal Lună +deschide timp actual în secunde navigare afișați subtitrările +document matematică Altele... +alertă , începând de pe +celulă +meniu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ru.xtb b/chromium/content/app/strings/translations/content_strings_ru.xtb index fd0d08c5ac7..6e81a7ae063 100644 --- a/chromium/content/app/strings/translations/content_strings_ru.xtb +++ b/chromium/content/app/strings/translations/content_strings_ru.xtb @@ -2,22 +2,30 @@ нет данных +элемент меню отключение звуковой дорожки кнопка-переключатель Секунды Введите адрес электронной почты. дополнительно +marquee +ползунок Очистить недавние поиски +диалоговое окно article баннер регион В этом месяце Выбрать файлы +индикатор статуса занято Введите адрес электронной почты. Другое… +диалоговое окно оповещения +сетка в виде дерева AM/PM воспроизвести на удаленном устройстве выбрать +внешние границы дд статус Максимальное значение должно быть . @@ -26,8 +34,10 @@ Длина текста не должна превышать симв. (сейчас симв.). Неделя , флажок +окно выбора даты и времени День Заполните это поле. +список вкладок Нет недавних поисков воспроизведение Введите допустимое значение. Ближайшее допустимое значение: . @@ -35,16 +45,25 @@ нижний колонтитул аудио Минимальное значение должно быть . +всплывающая кнопка Чтобы продолжить, установите этот флажок. 1024 (Средний размер) +группа переключателей текущий статус ролика Введите адреса электронной почты через запятую. +нажать +объект выделенный контент ссылка Введите верное значение. Поле не заполнено или введена недействительная дата. +таблица +note Введите URL. +таймер +поле со списком Значение должно быть меньше или равно . описание +заголовок оставшееся время в секундах переключить мм @@ -52,60 +71,89 @@ запрос Минимально допустимое количество символов: . Длина текста сейчас: . Часть адреса до символа "" не должна содержать символ "". +приложение +полоса прокрутки Миллисекунды +изображение Показать предыдущий месяц перейти +color picker +панель меню +сведения о контенте Введите допустимое значение. Ближайшие допустимые значения: и . +кнопка меню Подробнее форма +tree Часть адреса после символа "" не должна содержать символ "". +панель вкладок видео +поиск Введите число. Выбрано: Введите часть адреса после символа "". Адрес "" неполный. +индикатор хода выполнения маркер списка Показать следующий месяц +заголовок строки Значение должно быть больше или равно . Введите часть адреса до символа "". Адрес "" неполный. +Сохранить фигура +элемент дерева Выберите один или несколько файлов. выход из полноэкранного режима снять галочку +окно выбора даты включение звука +time пауза +кнопка Другое… Сбросить 2048 (Крупный размер) Неделя -адрес +кнопка счетчика Год +список включение звуковой дорожки активировать +счетчик бегунок временной шкалы бегунок полосы прокрутки Показать панель выбора месяца полоса воспроизведения +окно выбора даты полноэкранный режим +log Очистить Недавние поиски Другой... счетчик +линейка +подсказка Не удалось загрузить плагин отключить звук нажать скрыть субтитры +каталог +цитата продолжительность ролика Введите данные в указанном формате. прошедшее время Выберите один из пунктов списка. +заголовок столбца Выберите файл Другое… +tab Часы оставшееся время скрыть субтитры HTML-содержание Пауза +треугольник развертывания Адрес электронной почты должен содержать символ "". В адресе "" отсутствует символ "". +разделитель Файл не выбран поле поиска показать субтитры @@ -116,8 +164,11 @@ графическая карта Недопустимые данные. поставить галочку +панель инструментов Число файлов: Выберите файл. +переключатель +кнопка раскрывающегося меню Минуты управление мультимедиа список описаний @@ -127,10 +178,15 @@ начать воспроизведение main Месяц +открыть текущее время в секундах навигация показать субтитры +документ math Другое… +оповещение , начинается +ячейка +меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sk.xtb b/chromium/content/app/strings/translations/content_strings_sk.xtb index a647f5ade49..ca0376aaf70 100644 --- a/chromium/content/app/strings/translations/content_strings_sk.xtb +++ b/chromium/content/app/strings/translations/content_strings_sk.xtb @@ -2,22 +2,30 @@ prázdne +položka ponuky stlmiť zvukovú stopu tlačidlo prepínania Sekundy Zadajte e-mailovú adresu. doplnkové +pohyblivý prvok +posúvač Vyčistiť posledné vyhľadávania +dialóg článok banner oblasť Tento mesiac Vybrať súbory +indikátor zaneprázdnenosti Zadajte e-mailovú adresu, ktorá nie je prázdna hodnota. Iné... +dialóg_upozornenia +stromová mriežka AM / PM prehrať na vzdialenom zariadení vybrať +osnova dd stav Hodnota musí byť alebo skôr. @@ -26,8 +34,10 @@ Tento text musíte skrátiť na znakov alebo menej (súčasný počet znakov: ). . týždeň, začiarkavacie políčko +výber dátumu a času Deň Vyplňte toto pole. +zoznam kariet Žiadne posledné vyhľadávania prehrať Zadajte platnú hodnotu. Najbližšia platná hodnota je . @@ -35,16 +45,25 @@ päta zvuk Hodnota musí byť alebo neskôr. +kontextové tlačidlo Ak chcete pokračovať, začiarknite toto políčko. 1024 (stredný stupeň) +skupina prepínačov aktuálny stav filmu Zadajte zoznam e-mailových adries oddelených čiarkou. +kliknutie +objekt zvýraznený obsah odkaz Zadajte platnú hodnotu. Pole obsahuje neúplnú hodnotu alebo neplatný dátum. +tabuľka +poznámka Zadajte webovú adresu. +časovač +pole s výberom Hodnota musí byť menšia alebo rovná hodnote . definícia +nadpis zostávajúci čas do konca filmu v sekundách prepínač mm @@ -52,60 +71,89 @@ výraz Predĺžte tento text aspoň na alebo viac znakov (momentálny počet znakov je ). Časť pred znakom by nemala obsahovať symbol . +aplikácia +posúvač Milisekundy +grafika Zobraziť predchádzajúci mesiac skok +výber farieb +panel ponuky +informácie o obsahu Zadajte platnú hodnotu. Najbližšie platné hodnoty sú a . +tlačidlo ponuky Podrobnosti formulár +strom Časť za znakom by nemala obsahovať symbol . +panel karty video +search Zadajte číslo. Počet vybraných položiek: Zadajte časť za znakom . Adresa je neúplná. +indikátor priebehu ukazovateľ v zozname Zobraziť ďalší mesiac +hlavička riadka Hodnota musí byť väčšia alebo rovná hodnote . Zadajte časť pred znakom . Adresa je neúplná. +Uložiť hodnota +položka stromu Vyberte jeden alebo viac súborov. ukončiť režim celej obrazovky zrušiť označenie +výber dátumu obnoviť zvuk +čas pozastaviť prehrávanie +tlačidlo Iné... Obnoviť 2048 (vysoký stupeň) Týždeň -adresa +tlačidlo otáčania Rok +pole s ponukou obnoviť zvukovú stopu aktivovať +meter časová os filmu posúvač času filmu Zobraziť panel na výber mesiaca posúvač časovej osi zvuku +výber času prejsť do režimu celej obrazovky +denník Vymazať Posledné vyhľadávania Iný... stepper +pravítko +popis Doplnok sa nepodarilo načítať. stlmiť stlačiť skryť skryté titulky +adresár +značka blockquote čas filmu Zadajte hodnotu zodpovedajúcu požadovanému formátu. uplynutý čas Vyberte položku zo zoznamu. +hlavička stĺpca Vybrať súbor Iné... +karta Hodiny zostávajúci čas ukončiť zobrazovanie skrytých titulkov Obsah HTML pozastaviť +trojuholníkové tlačidlo na zobrazenie skrytého obsahu Uveďte v e-mailovej adrese znak . V adrese znak chýba. +rozdeľovač Nie je vybratý žiadny súbor textové pole pre vyhľadávanie zobrazovať skryté titulky @@ -116,8 +164,11 @@ mapa obrázka Neplatná hodnota. označiť +panel s nástrojmi Počet súborov: Vyberte súbor. +prepínač +tlačidlo rozbaľovacej ponuky Minúty ovládanie médií zoznam definícií @@ -127,10 +178,15 @@ začať prehrávanie hlavné Mesiac +otvorenie aktuálny čas v sekundách navigácia zobraziť skryté titulky +dokument matematika Iné... +upozornenie začínajúci +bunka +ponuka \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sl.xtb b/chromium/content/app/strings/translations/content_strings_sl.xtb index cbbae1fad79..310155830a2 100644 --- a/chromium/content/app/strings/translations/content_strings_sl.xtb +++ b/chromium/content/app/strings/translations/content_strings_sl.xtb @@ -2,22 +2,30 @@ prazno +menijski element izklop zvoka za zvočni posnetek preklopni gumb Sekunde Vnesite e-poštni naslov dopolnilno +potujoči napis +drsnik Počisti zadnja iskanja +pogovorno okno člnk pasica območje Ta mesec Izberi datoteke +kazalnik zasedenosti Vnesite e-poštni naslov (polje ne sme biti prazno). Drugo ... +pogovorno okno z opozorilom +drevesna mreža Dopoldne/popoldne predvajanje v oddaljeni napravi izberi +oris dd stanje Vrednost mora biti ali prej. @@ -26,8 +34,10 @@ Skrajšajte to besedilo na znakov ali manj (trenutno uporabljate znakov). . teden, potrditveno polje +izbirnik datuma in ure Dan Izpolnite to polje +seznam zavihkov Ni zadnjih iskanj predvajanje Vnesite veljavno vrednost. Najbližja veljavna vrednost je . @@ -35,16 +45,25 @@ noga zvok Vrednost mora biti ali pozneje. +pojavni gumb Potrdite to polje, če želite nadaljevati. 1024 (srednja stopnja) +skupina izbirnih gumbov trenutno stanje filma Vnesite seznam e-poštnih naslovov, ločenih z vejicami +klikniti +predmet označena vsebina povezava Vnesite veljavno vrednost. Vnos v polje je nepopoln ali vsebuje neveljaven datum. +tabela +opmb Vnesite URL. +merilnik časa +kombinirano polje Vrednost mora biti manjša od ali enaka. opredelitev +naslov preostale sekunde filma preklop mm @@ -52,60 +71,89 @@ izraz Podaljšajte to besedilo na toliko znakov ali več: (trenutno uporabljate toliko znakov: ). Del pred »« ne sme vsebovati znaka »«. +aplikacija +drsni trak Milisekunde +grafični element Prikaz prejšnjega meseca skoči +izbirnik barve +menijska vrstica +podatki o vsebini Vnesite veljavno vrednost. Najbližji veljavni vrednosti sta in . +menijski gumb Podrobnosti obrazec +drevo Del po »« ne sme vsebovati znaka »«. +podokno z zavihki video +search Vnesite številko. Št. izbranih: Vnesite nekaj po znaku »«. Naslov »« je nepopoln. +kazalnik poteka označevalnik seznama Prikaz naslednjega meseca +glava vrstice Vrednost mora biti večja od ali enaka. Vnesite nekaj in nato . Naslov »« je nepopoln. +Shrani številka +element drevesa Izberite eno ali več datotek. izhod iz celozaslonskega načina počisti izbor +izbirnik datuma vklop zvoka +čas začasna ustavitev predvajanja +gumb Drugo ... Ponastavi 2048 (visoka stopnja) Teden -naslov +pomikalnik Leto +polje s seznamom vklop zvoka za zvočni posnetek aktiviraj +merilnik sličica časovne premice filma sličica časovnega krmilnika za predvajanje filma Prikaz podokna za izbiro meseca časovni krmilnik za predvajanje zvoka +izbirnik ure prehod v celozaslonski način +dnevn Počisti Zadnja iskanja Drugo ... kontrolnik +ravnilo +opis orodja Vtičnika ni bilo mogoče naložiti. nemo pritisni skrivanje podnapisov +imenik +daljši citat čas filma Poskrbite za ujemanje z zahtevano obliko. pretečeni čas Izberite element s seznama. +glava stolpca Izberite datoteko Drugo ... +tabulatorka Ure preostali čas ustavitev prikazovanja podnapisov Vsebina HTML premor +trikotnik z dodatno vsebino V e-poštnem naslovu mora biti znak »«. V naslovu »« manjka »«. +razdelilnik Nobena datoteka ni izbrana besedilno polje za iskanje začetek predvajanja podnapisov @@ -116,8 +164,11 @@ slikovni zemljevid Neveljavna vrednost. potrdi +orodna vrstica Število datotek: Izberite datoteko. +izbirni gumb +spustni gumb Minute nadziranje predstavnosti seznam opredelitev @@ -127,10 +178,15 @@ začetek predvajanja glavn Mesec +odpreti trenutni čas v sekundah krmarjenje prikaz podnapisov +dokument matematika Drugo ... +opozorilo , začne se +celica +meni \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sr.xtb b/chromium/content/app/strings/translations/content_strings_sr.xtb index 50a88407dec..f064fdc51ff 100644 --- a/chromium/content/app/strings/translations/content_strings_sr.xtb +++ b/chromium/content/app/strings/translations/content_strings_sr.xtb @@ -2,22 +2,30 @@ празно +ставка менија искључите звук аудио снимка дугме за укључивање/искључивање Секунде Унесите имејл адресу. комплементарно +покретни текст +клизач Обриши недавне претраге +дијалог article банер регион Овај месец Избор датотека +индикатор да је заузето Унесите имејл адресу која није празна. Друго... +дијалог обавештења +мрежа стабла пре подне/по подне пуштајте на удаљеном уређају изабери +граница дд статус Вредност мора да буде или старија. @@ -26,8 +34,10 @@ Скратите овај текст на знак(ов)а или мање (тренутно користите знак(ов)а). . недеља, . поље за потврду +бирач датума и времена Дан Попуните ово поље. +листа картица Нема недавних претрага пустите Унесите важећу вредност. Најближа важећа вредност је . @@ -35,16 +45,25 @@ подножје аудио Вредност мора да буде или новија. +искачуће дугме Потврдите избор у овом пољу за потврду уколико желите да наставите. 1024 (средњи степен) +група дугмади за избор актуелни статус филма Унесите листу имејл адреса раздвојених зарезима. +клик +објекат истакнути садржај веза Унесите важећу вредност. Поље није попуњено или садржи неважећи датум. +табела +белешка Унесите URL адресу. +тајмер +комбиновани оквир Вредност сме да буде највише . дефиниција +заглавље број преосталих секунди филма прекидач мм @@ -52,60 +71,89 @@ термин Продужите овај текст на знак(ов)а или више (тренутно користите знак(ов)а). Део пре „“ не треба да садржи симбол „“. +апликација +трака за померање Милисекунде +графички елемент Прикажи претходни месец прескочи +бирач боја +трака са менијима +информације о садржају Унесите важећу вредност. Две најближе важеће вредности су и . +дугме менија Детаљи образац +стабло Део после „“ не треба да садржи симбол „“. +табла са картицама видео +search Унесите број. Изабрано: Унесите неки део после „“. Адреса „“ је непотпуна. +индикатор напретка означивач листе Прикажи следећи месец +заглавље реда Вредност сме да буде најмање . Унесите неки део пре „“. Адреса „“ је непотпуна. +Сачувај цифра +ставка стабла Изаберите једну или више датотека. изађите из режима целог екрана опозови избор +бирач датума укључите звук +време паузирајте репродукцију +дугме Друго... Ресетуј 2048 (високи степен) Недеља -адреса +дугме за промену вредности Година +оквир са листом укључите звук аудио снимка активирај +мерач клизач на временској траци филма клизач за трајање филма Прикажи таблу за избор месеца клизач за трајање аудио-садржаја +бирач времена пређите на режим целог екрана +log Обриши Недавне претраге Друго... контрола за промене у корацима +лењир +објашњење Учитавање додатне компоненте није успело. искључи звук притисни сакријте опционални титл +каталог +издвојени цитат трајање филма Изаберите захтевани формат. протекло време Изаберите ставку са листе. +заглавље колоне Одабери датотеку Друго... +tab Сати преостало време зауставите приказивање опционалног титла HTML садржај паузирај +троугао за откривање Уврстите „“ у имејл адресу. У адреси е-поште „“ недостаје „“. +разделник Није одабрано поље за текст претраге започните приказивање опционалног титла @@ -116,8 +164,11 @@ мапа слике Неважећа вредност. изабери +трака с алаткама датотеке(а) Изаберите датотеку. +дугме за избор +дугме за падајући мени Минути контрола за медије листа дефиниција @@ -127,10 +178,15 @@ започните репродукцију main Месец +отварање актуелно време у секундама навигација приказивање опционалног титла +документ математички Друго... +обавештење , од +ћелија +мени \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sv.xtb b/chromium/content/app/strings/translations/content_strings_sv.xtb index 22ec19c447e..8e6c8a4b857 100644 --- a/chromium/content/app/strings/translations/content_strings_sv.xtb +++ b/chromium/content/app/strings/translations/content_strings_sv.xtb @@ -2,22 +2,30 @@ tomt +menyalternativ stäng av ljudspår växlingsknapp Sekunder Ange en e-postadress. kompletterande +markör +skjutreglage Rensa senaste sökningar +dialogruta artikel banner område Den här månaden Välj filer +upptaget-indikator Ange en e-postadress som inte är tom. Annat ... +varningsdialogruta +träddiagram FM/EM spela på en fjärrenhet välj +översikt dd status Värdet måste vara eller tidigare. @@ -26,8 +34,10 @@ Förkorta texten till tecken eller mindre (nu är texten tecken). Vecka kryssruta +datum- och tidsväljare Dag Fyll i det här fältet. +fliklista Inga nya sökningar spela upp Ange ett giltigt värde. Det närmast giltiga värdet är . @@ -35,16 +45,25 @@ sidfot ljud Värdet måste vara eller senare. +popup-knapp Markera den här kryssrutan om du vill fortsätta. 1024 (medel) +alternativknappsgrupp aktuell filmstatus Ange en kommaavgränsad lista med e-postadresser. +klicka +objekt markerat innehåll länk Ange ett giltigt värde. Fältet är ofullständigt eller innehåller ett ogiltigt datum. +tabell +anm Ange en webbadress. +timer +kombinationsruta Värdet måste vara mindre än eller lika med . definition +rubrik antal sekunder kvar av filmen växel mm @@ -52,60 +71,89 @@ term Lägg till minst tecken (för närvarande har du angett tecken). En del följt av får inte innehålla symbolen . +program +rullningslist Millisekunder +bild Visa föregående månad fortsätta +färgval +menyfält +innehållsinformation Ange ett giltigt värde. De två närmaste giltiga värdena är och . +menyknapp Info formulär +träd En del efter får inte innehålla symbolen . +flikpanel video +sök Ange ett nummer. valda Ange en del följt av . är ofullständig. +förloppsindikator listmarkör Visa nästa månad +radrubrik Värdet måste vara större än eller lika med . Ange en del följt av . är ofullständig. +Spara figur +trädobjekt Välj en eller flera filer. avsluta helskärmsläge kryssa av +datumväljare visa +tid pausa uppspelning +knapp Annat ... Återställ 2048 (hög) Vecka -adress +snurrknapp År +listruta spela upp ljudspåret aktivera +mätare miniatyr för filmtidslinje miniatyr för filmtidsreglage Visa panelen för val av månad tidsreglage för ljud +tidsväljare visa i helskärm +logg Rensa Senaste sökningar Annan... knappreglage +linjal +beskrivning Det gick inte att läsa in plugin-programmet. ljud av tryck dölj textning +katalog +citatblock filmtid Matcha det format som anges. förfluten tid Välj ett alternativ i listan. +kolumnrubrik Välj fil Annat ... +tabb Timmar återstående tid sluta visa textning HTML-innehåll paus +expanderingstriangel Inkludera ett i e-postadressen. saknar ett . +delare Ingen fil har valts fält för söktext börja visa textning @@ -116,8 +164,11 @@ bildkarta Ogiltigt värde. kryssa för +verktygsfält filer Välj en fil. +alternativknapp +rullgardinsknapp Minuter mediekontroll definitionslista @@ -127,10 +178,15 @@ starta uppspelning huvud Månad +öppna aktuell tid i sekunder navigering visa textning +dokument matte Annat ... +varning som börjar den +cell +meny \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sw.xtb b/chromium/content/app/strings/translations/content_strings_sw.xtb index d18396819ac..8c0325a7f42 100644 --- a/chromium/content/app/strings/translations/content_strings_sw.xtb +++ b/chromium/content/app/strings/translations/content_strings_sw.xtb @@ -2,22 +2,30 @@ tupu +kipengee cha menyu nyamazisha sauti ya wimbo kitufe cha kugeuza Sekunde Tafadhali weka anwani ya barua pepe. nyongeza +marquee +kitelezi Futa Utafutaji wa Hivi Karibuni +kidirisha makala bango eneo Mwezi huu Chagua Faili +kiashiria cha shughuli Tafadhali jaza sehemu ya anwani ya barua pepe. Nyingine... +kidirisha cha arifa +gridi ya mti AM / PM cheza kwenye kifaa cha mbali chagua +muhtasari dd hali Thamani lazima iwe au mapema. @@ -26,8 +34,10 @@ Tafadhali fupisha maandishi haya hadi vibambo au chini (kwa sasa unatumia vibambo ). Wiki , kisanduku cha kuteua +mchumaji tarehe na wakati Siku Tafadhali jaza sehemu hii. +orodha ya vichupo Hakuna utafutaji wa hivi karibuni cheza Tafadhali ingiza thamani halali.Thamani halali ya karibu ni . @@ -35,16 +45,25 @@ kijachini sauti Thamani lazima iwe au baadaye. +kitufe cha dirisha ibukizi Tafadhali angalia kikasha hiki iwapo unataka kuendelea. 1024 (Gredi Wastani) +kikundi cha vitufe hali ya sasa ya filamu Tafadhali weka orodha ya anwani za barua pepe zilizotenganishwa kwa vikomo. +bofya +kitu maudhui yaliyoangaziwa kiungo Tafadhali ingiza thamani halali. Uga umekamilika au una tarehe batili. +jedwali +dokezo Tafadhali ingiza URL. +kipima muda +kisanduku mseto Lazima thamani iwe chache kuliko au sawa na . ufafanuzi +kichwa cha idadi ya sekunde zinazosalia za filamu badilisha mm @@ -52,60 +71,89 @@ neno Tafadhali refusha maandishi haya hadi herufi au zaidi (kwa sasa unatumia herufi ). Sehemu inayofuatwa na '' haipaswi kuwa na alama ya ''. +programu +sehemu ya kusogeza nukta +mchoro Onyesha mwezi uliotangulia ruka +kiteua rangi +upau wa menyu +maelezo ya maudhui Tafadhali ingiza thamani halali. Thamani mbili halali za karibu ni na . +kitufe cha menyu Maelezo fomu +mti Sehemu inayofuata '' haipaswi kuwa na alama ya ''. +kisanduku cha kichupo video +tafuta Tafadhali ingiza nambari. Vipengee vimechaguliwa Tafadhali ingiza sehemu inayofuatia ''. '' haijakamilika. +kiashiria cha maendeleo kialamishi orodha Onyesha mwezi unaofuata +kichwa cha safu mlalo Lazima thamani iwe kubwa kuliko au sawa na . Tafadhali ingiza sehemu ikifuatiwa na ''. '' haijakamilika. +Hifadhi umbo +kipengee cha mti Tafadhali chagua faili moja au zaidi. Ondoka kwenye Skrini nzima toa tiki +kichagua tarehe washa sauti +wakati sitisha kucheza +kitufe Nyingine... Weka upya 2048 (Gredi ya Juu) Juma -anwani +kitufe cha kubadilishia Mwaka +kikasha cha orodha washa sauti ya wimbo wezesha +mita kijipicha cha muda wa filamu kijipicha cha kitelezi cha muda wa filamu Onyesha kisanduku cha uchaguzi wa mwezi kitelezi cha muda cha sauti +Kiteua wakati ingia skrini kamili +kumbukumbu Futa Utafutaji wa hivi karibuni Mengine... stepper +rula +kidirisha cha vidokezo Haikuweza kupakia programu-jalizi. nyamazisha bofya ficha manukuu yanayoweza kuonyeshwa +saraka +nukuu la msingi muda wa filamu Tafadhali linganisha umbizo lililoombwa. muda uliokwisha Tafadhali chagua kipengee katika orodha. +kijajuu cha safu wima Chagua Faili Nyingine... +kichupo Saa muda unaosalia koma kuonyesha manukuu yanaweza kufichwa Maudhui ya HTML Sitisha +pembe tatu ya ufafanuzi Tafadhali jumuisha '' katika anwani ya barua pepe. '' inakosa ''. +kitenganishi Hakuna faili iliyochaguliwa sehemu ya maandishi ya utafutaji anza kuonyesha manukuu yaliyofungwa @@ -116,8 +164,11 @@ ramani ya picha Thamani batili. chunguza +upau wa vidhibiti faili Tafadhali chagua faili. +kitufe cha mviringo +kitufe kunjuzi Dakika udhibiti wa vyombo vya habari orodha ya ufafanuzi @@ -127,10 +178,15 @@ anza kucheza kuu Mwezi +fungua muda wa sasa kwa sekunde kuvinjari onyesha manakuu yaliyofichwa +hati hisabati Nyingine... +arifa , itaanza tarehe +kisanduku +menyu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ta.xtb b/chromium/content/app/strings/translations/content_strings_ta.xtb index c77fc832f89..ce308d46fc5 100644 --- a/chromium/content/app/strings/translations/content_strings_ta.xtb +++ b/chromium/content/app/strings/translations/content_strings_ta.xtb @@ -2,22 +2,30 @@ வெற்று +மெனு உருப்படி ஆடியோ டிராக்கை முடக்கு நிலைமாற்றுதல் பொத்தான் வினாடிகள் ஒரு மின்னஞ்சல் முகவரியை உள்ளிடவும். ஈடுசெய்யக்கூடியது +மார்கியூ +ஸ்லைடர் சமீபத்திய தேடல்களை சுத்தமாக்கு +உரையாடல் article பேனர் மண்டலம் இந்த மாதம் கோப்புகளைத் தேர்வுசெய்க +பணிமிகுதி குறிப்பான் காலி அல்லாத மின்னஞ்சல் முகவரியை உள்ளிடவும். மற்றவை… +விழிப்பூட்டல்_உரையாடல் +ட்ரீ கிரிட் AM/PM தொலைநிலைச் சாதனத்தில் இயக்கு தேர்ந்தெடு +சுற்றுவரி dd நிலை மதிப்பு அல்லது அதற்கு முன்பு இருக்க வேண்டும். @@ -26,8 +34,10 @@ இந்த உரையை எழுத்துக்குறிகள் அல்லது அதற்கும் குறைவாக சுருக்கிடுங்கள் (நீங்கள் தற்போது எழுத்துக்குறிகளைப் பயன்படுத்துகிறீர்கள்). வாரம் , செக்பாக்ஸ் +தேதி மற்றும் நேரம் தேர்ந்தெடுப்பான் நாள் இந்தப் புலத்தை நிரப்புக. +தாவல் பட்டியல் சமீபத்திய தேடல்கள் எதுவுமில்லை இயக்கு சரியான மதிப்பை உள்ளிடவும். என்பது நெருக்கமாக உள்ள சரியான மதிப்பாகும். @@ -35,16 +45,25 @@ அடிக்குறிப்பு ஆடியோ மதிப்பானது அல்லது அதற்குப் பின்பு இருக்க வேண்டும். +பாப்-அப் பொத்தான் தொடர விரும்பினால், இந்தப் பெட்டியைத் தேர்ந்தெடுங்கள். 1024 (இடைநிலைத் தரம்) +ரேடியோ குழு மூவியின் நடப்பு நிலை காற்புள்ளியால் பிரிக்கப்பட்ட மின்னஞ்சல் முகவரிகளின் பட்டியலை உள்ளிடுக. +கிளிக் செய்க +பொருள் தனிப்படுத்தப்பட்ட உள்ளடக்கம் இணைப்பு சரியான மதிப்பை உள்ளிடவும். இந்தப் புலம் முழுமையற்றதாக உள்ளது அல்லது தவறான தேதியைக் கொண்டுள்ளது. +அட்டவணை +குறிப்பு URL ஐ உள்ளிடுக. +டைமர் +சேர்க்கைப் பெட்டி மதிப்பானது கண்டிப்பாக ஐ விடக்குறைவாக அல்லது அதற்குச் சமமாக இருக்க வேண்டும். விளக்கம் +தலைப்பு மீதமுள்ள மூவியின் வினாடிகள் ஸ்விட்ச் mm @@ -52,60 +71,89 @@ சொல் இந்த உரையை எழுத்துக்குறிகள் அல்லது அதற்கும் அதிகமாக (தற்போது எழுத்துக்குறிகளைப் பயன்படுத்துகிறீர்கள்) நீட்டிக்கவும். '' ஐத் தொடர்ந்து வரும் பகுதியில் '' சின்னம் இருக்கக்கூடாது. +பயன்பாடு +உருட்டல் பட்டி மில்லிவினாடிகள் +கிராஃபிக் முந்தைய மாதத்தைக் காட்டு தாவு +வண்ணத் தேர்வி +மெனுப் பட்டி +உள்ளடக்கத் தகவல் சரியான மதிப்பை உள்ளிடவும். மற்றும் ஆகியவை மிக நெருக்கமான சரியான இரண்டு மதிப்புகளாகும். +மெனு பொத்தான் விவரங்கள் படிவம் +tree '' ஐத் தொடரும் பகுதியில், '' சின்னம் இருக்கக்கூடாது. +தாவல் பலகம் வீடியோ +Search எண்ணை உள்ளிடுக. தேர்ந்தெடுக்கப்பட்டன '' ஐத் தொடர்ந்து ஒரு பகுதியை உள்ளிடவும். '' முழுமைப்பெறாமல் உள்ளது. +செயல்நிலை காட்டி பட்டியல் குறிப்பான் அடுத்த மாதத்தைக் காட்டு +வரிசை மேற்தலைப்பு மதிப்பானது, கண்டிப்பாக ஐ விட அதிகமாக அல்லது அதற்குச் சமமாக இருக்க வேண்டும். '' ஐத் தொடர்ந்து ஒரு பகுதியை உள்ளிடவும். '' முழுமைப் பெறாமல் உள்ளது. +சேமி உருவம் +ட்ரீ உருப்படி ஒன்று அல்லது அதற்குமேற்பட்ட கோப்புகளைத் தேர்ந்தெடுங்கள். முழுத்திரையிலிருந்து வெளியேறு தேர்வு நீக்கு +தேதி தேர்ந்தெடுப்பான் ஒலி இயக்கு +நேரம் மறுஇயக்கத்தை இடைநிறுத்து +பொத்தான் மற்றவை… மீட்டமை 2048 (உயர் தரம்) வாரம் -முகவரி +சுழல் பொத்தான் ஆண்டு +பட்டியல் பெட்டி ஆடியோ டிராக்கை இயக்கு செயல்படுத்து +மீட்டர் மூவி டைம்லைன் சிறுபடம் மூவி நேர ஸ்க்ரப்பர் சிறுபடம் மாதம் தேர்ந்தெடுப்புப் பலகத்தைக் காட்டு ஆடியோ நேர ஸ்கிரப்பர் +நேரம் தேர்ந்தெடுப்பான் முழுத்திரைக்குச் செல் +பதிவு அழி சமீபத்திய தேடல்கள் மற்றவை… ஸ்டெப்பர் +அளவுகோல் +உதவிக்குறிப்பு செருகுநிரல் ஏற்றப்படவில்லை. ஒலியடக்கு அழுத்துக மூடப்பட்ட தலைப்புகளை மறை +கோப்பகம் +பிளாக்கோட் மூவி நேரம் கோரிய வடிவமைப்பில் தருக. முடிவடைந்த நேரம் பட்டியலிலிருந்து ஒரு உருப்படியைத் தேர்ந்தெடுங்கள். +நெடுவரிசை மேற்தலைப்பு கோப்பைத் தேர்வு செய்க மற்றவை… +tab மணிநேரம் மீதமுள்ள நேரம் மூடப்பட்ட தலைப்புகளைக் காட்டுவதை நிறுத்து HTML உள்ளடக்கம் இடைநிறுத்து +கூடுதல் உள்ளடக்க முக்கோணம் மின்னஞ்சல் முகவரியில் '' ஐச் சேர்க்கவும். '' இல் '' இல்லை. +பிரிப்பான் எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை தேடல் உரைப் புலம் மூடப்பட்ட தலைப்புகளைக் காட்டுவதைத் தொடங்கு @@ -116,8 +164,11 @@ பட மேப் செல்லாத மதிப்பு. சரிபார் +கருவிப்பட்டி கோப்புகள் ஒரு கோப்பை தேர்ந்தெடுக்கவும். +ரேடியோ பொத்தான் +கீழ் தோன்றுதல் மெனு நிமிடங்கள் மீடியா கட்டுப்பாடு விளக்கப் பட்டியல் @@ -127,10 +178,15 @@ மறுஇயக்கத்தைத் தொடங்கு முதன்மை மாதம் +திற நடப்பு நேரம் வினாடிகளில் வழிசெலுத்தல் மூடப்பட்ட தலைப்புகளைக் காட்டு +ஆவணம் கணிதம் மற்றவை… +விழிப்பூட்டல் , அன்று தொடங்குவது +கலம் +மெனு \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_te.xtb b/chromium/content/app/strings/translations/content_strings_te.xtb index 42506b219ff..866daee235c 100644 --- a/chromium/content/app/strings/translations/content_strings_te.xtb +++ b/chromium/content/app/strings/translations/content_strings_te.xtb @@ -2,22 +2,30 @@ ఖాళీ +మెను అంశం ఆడియో ట్రాక్‌ను మ్యూట్ చేయి టోగుల్ బటన్ సెకన్లు దయచేసి ఇమెయిల్ చిరునామాను ఎంటర్ చెయ్యండి. బహుమానపూర్వకం +marquee +స్లయిడర్ ఇటీవల శోధనలను క్లియర్ చెయ్యి +డైలాగ్ కథనం బ్యానర్ ప్రాంతం ఈ నెల ఫైల్‌లను ఎంచుకోండి +బిజీ సూచిక దయచేసి ఖాళీ-కాని ఇమెయిల్ చిరునామాని నమోదు చేయండి. ఇతర... +alert_dialog +వృక్షాంశ గ్రిడ్ AM/PM రిమోట్ పరికరంలో ప్లే చేస్తుంది ఎంచుకోండి +చుట్టుగీత dd స్థితి విలువ తప్పనిసరిగా లేదా అంతకంటే మునుపటిది అయి ఉండాలి. @@ -26,8 +34,10 @@ దయచేసి ఈ వచనాన్ని అక్షరాలకు లేదా అంతకంటే తక్కువ (మీరు ప్రస్తుతం అక్షరాలను ఉపయోగిస్తున్నారు)కు తగ్గించండి. వ వారం, తనిఖీపెట్టె +తేదీ మరియు సమయం ఎంపిక రోజు దయచేసి ఈ ఫీల్డ్‌ని పూర్తి చెయ్యండి. +ట్యాబ్ జాబితా ఇటీవల శోధనలు లేవు ప్లే చేయి దయచేసి చెల్లుబాటు అయ్యే విలువను నమోదు చేయండి. అనేది సమీప చెల్లుబాటు విలువ. @@ -35,16 +45,25 @@ ఫుటర్ ఆడియో విలువ తప్పనిసరిగా లేదా అంతకంటే తదుపరిది అయి ఉండాలి. +పాప్ అప్ బటన్ దయచేసి మీరు కొనసాగాలనుకుంటే ఈ బాక్స్‌కి టిక్కు పెట్టండి. 1024 (మధ్యస్థ గ్రేడ్) +రేడియో సమూహం ప్రస్తుతం చలనచిత్ర స్థితి దయచేసి కామాతో వేరు చేసిన ఇమెయిల్ చిరునామాల జాబితాను ఎంటర్ చెయ్యండి. +క్లిక్ చేయి +ఆబ్జెక్ట్ ప్రముఖంగా చూపిన కంటెంట్ లింక్ దయచేసి చెల్లుబాటు అయ్యే విలువను నమోదు చేయండి. ఫీల్డ్ అసంపూర్ణంగా ఉంది లేదా చెల్లని తేదీని కలిగి ఉంది. +పట్టిక +note దయచేసి ఒక URLని ఎంటర్ చెయ్యండి. +టైమర్ +కాంబో పెట్టె విలువ ఖచ్చితంగా కంటే తగ్గువగా లేదా సమానంగా ఉండాలి. నిర్వచనం +ముఖ్య శీర్షిక చలనచిత్రంలో మిగిలి ఉన్న సెకన్లు మార్పు mm @@ -52,60 +71,89 @@ పదం దయచేసి ఈ వచనాన్ని లేదా అంతకంటే ఎక్కువ అక్షరాలకు పొడిగించండి (ప్రస్తుతం మీరు అక్షరాలను ఉపయోగిస్తున్నారు). ''కి ముందు ఉన్న భాగంలో '' చిహ్నం ఉండకూడదు. +అనువర్తనం +స్క్రోల్ పట్టీ మిల్లీసెకన్లు +గ్రాఫిక్ మునుపటి నెలను చూపుతుంది వెళ్ళు +రంగు ఎంపిక +మెను పట్టీ +కంటెంట్ సమాచారం దయచేసి చెల్లుబాటు అయ్యే విలువను నమోదు చేయండి. మరియు అనేవి రెండు సమీప చెల్లుబాటు విలువలు. +మెను బటన్ వివరాలు ఫారమ్ +tree ''కి తర్వాత ఉన్న భాగంలో '' చిహ్నం ఉండకూడదు. +ట్యాబ్ ప్యానెల్ వీడియో +search దయచేసి సంఖ్యను నమోదు చేయండి. ఎంచుకోబడ్డాయి దయచేసి ''కి తర్వాత ఉన్న భాగాన్ని నమోదు చేయండి. '' అసంపూర్ణంగా ఉంది. +ప్రోగ్రెస్ సూచిక జాబితా మార్కర్ తదుపరి నెలను చూపుతుంది +అడ్డు వరుస శీర్షిక విలువ ఖచ్చితంగా కంటే ఎక్కువగా లేదా సమానంగా ఉండాలి. దయచేసి ''కి ముందు ఉన్న భాగాన్ని నమోదు చేయండి. '' అసంపూర్ణంగా ఉంది. +సేవ్ చేయి రూపం +వృక్షాంశం దయచేసి ఒకటి లేదా మరిన్ని ఫైళ్ళను ఎంచుకోండి. పూర్తి స్క్రీన్ నుండి నిష్క్రమించు ఎంపిక చెయ్యబడలేదు +తేదీ ఎంపిక అన్‌మ్యూట్ చేయి +time ప్లేబ్యాక్‌ను పాజ్ చేయి +బటన్ ఇతర... రీసెట్ చేయి 2048 (ఉత్తమ గ్రేడ్) వారం -చిరునామా +స్పిన్ బటన్ సంవత్సరం +జాబితా పెట్టె ఆడియో ట్రాక్‌ను అన్‌మ్యూట్ చేయి ఆక్టివేట్ చెయ్యి +మీటర్ చలనచిత్ర టైమ్‌లైన్ సూక్ష్మచిత్రం చలనచిత్ర సమయ స్క్రబ్బర్ సూక్ష్మచిత్రం నెల ఎంపిక ప్యానెల్‌ను చూపుతుంది ఆడియో సమయ స్క్రబ్బర్ +సమయం ఎంపిక పూర్తి స్క్రీన్‌లోకి ప్రవేశించు +log క్లియర్ చేయి ఇటీవల శోధనలు ఇతర... స్టెప్పర్ +రూలర్ +సాధన చిట్కా ప్లగిన్‌ను లోడ్ చేయడం సాధ్యపడలేదు. మ్యూట్ చేయి నొక్కండి సంవృత శీర్షికలను దాచు +డైరెక్టరీ +బ్లాక్‌కోట్ చలనచిత్ర నిడివి దయచేసి అభ్యర్థించిన ఆకృతీకరణను సరిపోల్చండి. గడిచిన సమయం దయచేసి జాబితాలోని ఒక అంశాన్ని ఎంచుకోండి. +నిలువు వరుస శీర్షిక ఫైల్‌ను ఎంచుకోండి ఇతర... +tab గంటలు మిగిలి ఉన్న సమయం సంవృత శీర్షికలను ప్రదర్శించడం ఆపివేయి HTML కంటెంట్ నిలిపివేయి +కంటెంట్‌ను విస్తరింపజేసే లేదా కుదించే త్రిభుజం దయచేసి ఇమెయిల్ చిరునామాలో ''ని చేర్చండి. ''లో '' లేదు. +విభజన ఫైల్ ఏదీ ఎంచుకోలేదు శోధన వచనం ఫీల్డ్ సంవృత శీర్షికలను ప్రదర్శించడం ప్రారంభించు @@ -116,8 +164,11 @@ చిత్రం మ్యాప్‌ చెల్లని విలువ. తనిఖీ చెయ్యి +సాధన పట్టీ ఫైళ్ళు దయచేసి ఒక ఫైల్‌ని ఎంచుకోండి. +రేడియో బటన్ +డ్రాప్ డౌన్ బటన్ నిమిషాలు మీడియా నియంత్రణ నిర్వచన జాబితా @@ -127,10 +178,15 @@ ప్లేబ్యాక్‌ను ప్రారంభించు main నెల +తెరువు సెకన్లలో ప్రస్తుత సమయం నావిగేషన్ సంవృత శీర్షికలను చూపు +పత్రం math ఇతర... +హెచ్చరిక , నుండి ప్రారంభమవుతుంది +గడి +మెను \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_th.xtb b/chromium/content/app/strings/translations/content_strings_th.xtb index 946408959c6..34dedd0e0a4 100644 --- a/chromium/content/app/strings/translations/content_strings_th.xtb +++ b/chromium/content/app/strings/translations/content_strings_th.xtb @@ -2,22 +2,30 @@ ว่าง +รายการเมนู ปิดไฟล์เสียง ปุ่มสลับ วินาที โปรดป้อนที่อยู่อีเมล ส่วนเสริม +ตัวอักษรวิ่ง +แถบเลื่อน ลบการค้นหาล่าสุด +กล่องโต้ตอบ บทความ แบนเนอร์ ภูมิภาค เดือนนี้ เลือกไฟล์ +สัญญาณบอกสถานะไม่ว่าง โปรดป้อนที่อยู่อีเมลที่ไม่ว่างเปล่า อื่นๆ... +alert_dialog +แผนผังต้นไม้ AM/PM เล่นในอุปกรณ์ระยะไกล เลือก +เค้าโครง วว สถานะ ค่าต้องเป็น หรือก่อนหน้านั้น @@ -26,8 +34,10 @@ โปรดย่อข้อความนี้ให้เหลือไม่เกิน อักขระ (ขณะนี้ข้อความของคุณมี อักขระ) สัปดาห์ที่ , ช่องทำเครื่องหมาย +เครื่องมือเลือกวันที่และเวลา วัน โปรดกรอกฟิลด์นี้ +รายการแท็บ ไม่พบการค้นหาล่าสุด เล่น โปรดป้อนค่าที่ถูกต้อง ค่าใกล้เคียงที่สุดที่ถูกต้องคือ @@ -35,16 +45,25 @@ ส่วนท้าย เสียง ค่าต้องเป็น หรือหลังจากนั้น +ปุ่มป๊อปอัป โปรดเลือกช่องนี้หากคุณต้องการดำเนินการต่อ 1024 (เกรดปานกลาง) +กลุ่มตัวเลือก สถานะปัจจุบันของภาพยนตร์ โปรดป้อนรายการที่อยู่อีเมลโดยคั่นด้วยเครื่องหมายจุลภาค +คลิก +ออบเจ็กต์ เนื้อหาที่ไฮไลต์ ลิงก์ โปรดป้อนค่าที่ถูกต้อง ฟิลด์นี้ไม่สมบูรณ์หรือมีวันที่ที่ไม่ถูกต้อง +ตาราง +หมายเหตุ โปรดป้อน URL +ตัวจับเวลา +ช่องตัวเลือกรวม ค่าต้องน้อยกว่าหรือเท่ากับ คำจำกัดความ +ส่วนหัว จำนวนวินาทีที่เหลือของภาพยนตร์ สวิตช์ ดด @@ -52,60 +71,89 @@ คำหลัก โปรดกรอกข้อความนี้ให้มีอักขระอย่างน้อย ตัว (ตอนนี้คุณมี ตัว) ส่วนที่ตามด้วย "" ต้องไม่มีสัญลักษณ์ "" +แอปพลิเคชัน +แถบเลื่อน มิลลิวินาที +กราฟิก แสดงเดือนที่ผ่านมา ข้าม +ตัวเลือกสี +แถบเมนู +ข้อมูลเนื้อหา โปรดป้อนค่าที่ถูกต้อง ค่าใกล้เคียงที่สุดที่ถูกต้องสองรายการคือ และ +ปุ่มเมนู รายละเอียด ฟอร์ม +แผนผัง ส่วนที่ต่อท้าย "" ต้องไม่มีสัญลักษณ์ "" +แผงแท็บ วิดีโอ +ค้นหา โปรดป้อนตัวเลข เลือกไว้ รายการ โปรดป้อนส่วนที่ต่อท้าย "" "" นั้นไม่สมบูรณ์ +ตัวบอกสถานะความคืบหน้า ผู้สร้างรายการ แสดงเดือนถัดไป +ส่วนหัวของแถว ค่าต้องมากกว่าหรือเท่ากับ โปรดป้อนส่วนหนึ่งโดยตามด้วย "" "" นั้นไม่สมบูรณ์ +บันทึก ตัวเลข +รายการแบบต้นไม้ โปรดเลือกอย่างน้อยหนึ่งไฟล์ ออกจากการแสดงแบบเต็มหน้าจอ ยกเลิกการทำเครื่องหมาย +เครื่องมือเลือกวันที่ เปิดเสียง +เวลา หยุดเล่นชั่วคราว +ปุ่ม อื่นๆ... รีเซ็ต 2048 (เกรดสูง) สัปดาห์ -ที่อยู่ +ปุ่มหมุน ปี +ช่องรายการ เปิดไฟล์เสียง เปิดใช้งาน +เมตร เส้นเวลาขนาดย่อของภาพยนตร์ ตัวควบคุมเวลาขนาดย่อของภาพยนตร์ แสดงแผงการเลือกเดือน ตัวควบคุมเวลาของเสียง +เครื่องมือเลือกเวลา เข้าสู่โหมดเต็มหน้าจอ +บันทึก ล้าง การค้นหาล่าสุด อื่นๆ... ตัวเพิ่ม/ลดระดับเสียง +ไม้บรรทัด +เคล็ดลับเครื่องมือ ไม่สามารถโหลดปลั๊กอิน ปิดเสียง กด ซ่อนคำอธิบายภาพ +ไดเรกทอรี +ข้อความที่ยกมา เวลาของภาพยนตร์ โปรดจับคู่รูปแบบที่ร้องขอ เวลาที่ผ่านไป โปรดเลือกรายการจากหน้ารายการ +ส่วนหัวคอลัมน์ เลือกไฟล์ อื่นๆ... +แท็บ ชั่วโมง เวลาที่เหลือ หยุดแสดงคำอธิบายภาพ เนื้อหา HTML หยุดชั่วคราว +สามเหลี่ยมซ่อนเนื้อหา โปรดใส่ "" ในที่อยู่อีเมล "" ขาด "" +ตัวแยก ไม่ได้เลือกไฟล์ใด ช่องข้อความค้นหา เริ่มแสดงคำอธิบายภาพ @@ -116,8 +164,11 @@ แผนที่รูปภาพ ค่าไม่ถูกต้อง ทำเครื่องหมาย +แถบเครื่องมือ ไฟล์ โปรดเลือกไฟล์ +ปุ่มตัวเลือก +ปุ่มแบบเลื่อนลง นาที การควบคุมสื่อ รายการคำจำกัดความ @@ -127,10 +178,15 @@ เริ่มเล่น หลัก เดือน +เปิด เวลาปัจจุบันเป็นวินาที การนำทาง แสดงคำอธิบายภาพ +เอกสาร คณิตศาสตร์ อื่นๆ... +การแจ้งเตือน เริ่มวันที่ +เซลล์ +เมนู \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_tr.xtb b/chromium/content/app/strings/translations/content_strings_tr.xtb index 2294a5e03d0..0c255c34bb0 100644 --- a/chromium/content/app/strings/translations/content_strings_tr.xtb +++ b/chromium/content/app/strings/translations/content_strings_tr.xtb @@ -2,22 +2,30 @@ boş +menü öğesi ses kanalını kapat geçiş düğmesi Saniye Lütfen e-posta adresi girin. tamamlayıcı +marquee +kaydırma çubuğu Son Aramaları Temizle +iletişim kutusu article banner bölge Bu ay Dosyaları Seç +meşgul göstergesi Lütfen e-posta adresini boş bırakmayın. Diğer... +alert_dialog +ağaç tablo AM/PM uzaktan cihazda oynat seç +özet gg durum Değer veya daha geri olmalıdır. @@ -26,8 +34,10 @@ Lütfen bu metni veya daha az karakter olacak şekilde kısaltın (şu anda karakter kullanıyorsunuz). . hafta, onay kutusu +tarih ve saat seçici Gün Lütfen bu alanı doldurun. +sekme listesi Yeni arama yok oynat Lütfen geçerli bir değer girin. En yakın geçerli değer şudur: @@ -35,16 +45,25 @@ altbilgi ses Değer veya daha ileri olmalıdır. +pop-up düğmesi İlerlemek istiyorsanız lütfen bu kutuyu işaretleyin. 1024 (Orta Düzey) +radyo düğmesi grubu filmin geçerli durumu Lütfen e-posta adreslerinin virgülle ayrılmış listesini girin. +tıklama +nesne vurgulanan içerik bağlantı Lütfen geçerli bir tarih girin. Alan tam doldurulmamış veya geçersiz bir tarih var. +tablo +note Lütfen bir URL girin. +zamanlayıcı +birleşik kutu Değer veya daha küçük olmalıdır. tanım +başlık saniye olarak filmin kalan süresi anahtar aa @@ -52,60 +71,89 @@ terim Lütfen bu metni karakter veya daha fazla olacak şekilde uzatın (şu anda karakter kullanıyorsunuz). Başında "" bulunan kısımda "" simgesi bulunmamalıdır. +uygulama +kaydırma çubuğu Milisaniye +grafik Önceki ayı göster git +renk seçici +menü çubuğu +içerik bilgileri Lütfen geçerli bir değer girin. En yakın iki geçerli değer şunlardır: ve . +menü düğmesi Ayrıntılar form +tree Başında "" bulunan kısımda "" simgesi bulunmamalıdır. +sekme paneli video +ara Lütfen bir sayı girin. tane seçildi Lütfen başına "" ekleyin. "" eksik. +ilerleme durumu göstergesi liste işaretçisi Sonraki ayı göster +satır başlığı Değer veya daha büyük olmalıdır. Lütfen başına "" ekleyin. "" adresi eksik. +Kaydet figür +ağaç öğesi Lütfen bir veya daha fazla dosya seçin. tam ekrandan çık işareti kaldır +tarih seçici sesi aç +time oynatmayı duraklat +düğme Diğer... Sıfırla 2048 (Yüksek Düzey) Hafta -adres 1 +dönme düğmesi Yıl +liste kutusu ses kanalını aç etkinleştir +ölçüm aracı film zaman çizelgesi küçük resmi film süresi göstergesinin küçük resmi Ay seçim panelini göster ses zaman çizelgesi temizleyici +zaman seçici tam ekrana geç +log Temizle Son Aramalar Diğer... stepper (adımlayıcı) +cetvel +ipucu Eklenti yüklenemedi. sesi kapat bas alt yazıları gizle +dizin +blok alıntı filmin süresi Lütfen istenen biçimi eşleştirin. geçen süre Lütfen listeden bir öğe seçin. +sütun başlığı Dosya Seç Diğer... +tab Saat kalan süre altyazıların görüntülenmesini durdur HTML içeriği duraklat +açıklama üçgeni Lütfen e-posta adresine bir "" işareti ekleyin. "" adresinde "" eksik. +ayırıcı Dosya seçilmedi arama metni alanı altyazıları görüntülemeye başla @@ -116,8 +164,11 @@ resim haritası Geçersiz değer. işaretle +araç çubuğu dosya Lütfen bir dosya seçin. +radyo düğmesi +açılır liste düğmesi Dakika medya kontrolü tanım listesi @@ -127,10 +178,15 @@ oynatmayı başlat main Ay +açma saniye olarak geçerli süre gezinme alt yazıları göster +doküman math Diğer... +uyarı tarihinde başlayan +hücre +menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_uk.xtb b/chromium/content/app/strings/translations/content_strings_uk.xtb index 2de2adf0962..31e5c5b30db 100644 --- a/chromium/content/app/strings/translations/content_strings_uk.xtb +++ b/chromium/content/app/strings/translations/content_strings_uk.xtb @@ -2,22 +2,30 @@ порожнє +пункт меню вимкнути звукову доріжку перемикач Секунди Введіть електронну адресу. додатково +область виділення +повзунок Очистити останні пошуки +діалогове вікно стаття банер регіон Цей місяць Вибрати файли +індикатор зайнятості Введіть електронну адресу. Інші... +вікно сповіщення +сітка дерева д.п./п.п. відтворити на віддаленому пристрої вибрати +структура дд статус Має бути або раніша дата. @@ -26,8 +34,10 @@ Скоротіть текст до такої кількості символів або менше: (наразі використано символів: ). Тиждень , р. прапорець +засіб вибору дати й часу День Заповніть це поле. +cписок вкладок Немає останніх пошуків відтворити Введіть дійсне значення. Найближче дійсне значення: . @@ -35,16 +45,25 @@ нижній колонтитул аудіо Має бути або пізніша дата. +кнопка спливаючої підказки Поставте тут прапорець, якщо хочете продовжити. 1024 (Середній рівень) +група перемикачів поточний статус фільму Введіть список електронних адрес, розділених комою. +натиснути +об’єкт виділений вміст посилання Введіть дійсне значення. Поле не заповнено або введено недійсну дату. +таблиця +примітка Введіть URL-адресу. +таймер +поле зі списком Значення має бути меншим або дорівнювати . визначення +заголовок кількість секунд до кінця фільму перемикач мм @@ -52,60 +71,89 @@ термін У тексті має бути на менше символів (ви ввели символів). Частина перед знаком "" не може містити символ "". +додаток +смуга прокрутки Мілісекунди +зображення Показати попередній місяць перейти +вибір кольору +панель меню +інформація про вміст Введіть дійсне значення. Два найближчі дійсні значення: і . +кнопка меню Деталі форма +дерево Частина після знака "" не може містити символ "". +панель вкладок відео +пошук Введіть число. Вибрано Введіть частину електронної адреси після знака "". Електронна адреса "" неповна. +індикатор перебігу маркер списку Показати наступний місяць +заголовок рядка Значення має бути більшим або дорівнювати . Введіть частину електронної адреси до знака "". Електронна адреса "" неповна. +Зберегти фігура +елемент дерева Виберіть один або декілька файлів. вийти з повноекранного режиму зняти прапорець +засіб вибору дати увімкнути звук +час призупинити відтворення +кнопка Інші... Скинути 2048 (Високий рівень) Тиждень -адреса +лічильник Рік +вікно списку увімкнути звукову доріжку активувати +вимірювач ескіз на часовій шкалі фільму ескіз на повзунку перебігу фільму Показати панель вибору місяців повзунок часу відтворення аудіо +засіб вибору часу увійти в повноекранний режим +журнал Очистити Останні пошуки Інший... повторювач stepper +лінійка +підказка Не вдалося завантажити плагін. вимкнути звук натиснути сховати приховані субтирти +каталог +цитата тривалість фільму Виберіть потрібний формат. минуло часу Виберіть елемент зі списку. +заголовок стовпця Вибрати файл Інші... +вкладка Години залишилось часу сховати приховані субтитри Вміст HTML призупинити +трикутник відкривання Електронна адреса має містити знак "". В електронній адресі "" знака "" немає. +розділювач Файл не вибрано текстове поле пошуку показати приховані субтитри @@ -116,8 +164,11 @@ мапа зображення Недійсне значення установити прапорець +панель інструментів файлів: Виберіть файл. +перемикач +кнопка спадного меню Хвилини елемент керування мультимедіа список визначень @@ -127,10 +178,15 @@ розпочати відтворення головний Місяць +відкрити поточний час у секундах навігація показати приховані субтитри +документ математика Інші... +сповіщення , починається +клітинка +меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_vi.xtb b/chromium/content/app/strings/translations/content_strings_vi.xtb index 813546bb357..ba271ad5801 100644 --- a/chromium/content/app/strings/translations/content_strings_vi.xtb +++ b/chromium/content/app/strings/translations/content_strings_vi.xtb @@ -2,22 +2,30 @@ trống +mục menu tắt tiếng bản âm thanh nút chuyển đổi Giây Vui lòng nhập địa chỉ email. bổ sung +bảng chữ chạy +thanh trượt Xóa Tìm kiếm Gần đây +hộp thoại bài viết biểu ngữ khu vực Tháng này Chọn tệp +chỉ báo bận Vui lòng nhập địa chỉ email không trống. Khác... +alert_dialog +lưới dạng cây SA/CH phát trên thiết bị từ xa chọn +đường viền dd trạng thái Giá trị phải là hoặc sớm hơn. @@ -26,8 +34,10 @@ Hãy cắt ngắn văn bản này thành ký tự hoặc ít hơn (bạn hiện đang sử dụng ký tự). Tuần , hộp kiểm +bộ chọn ngày và giờ Ngày Vui lòng điền vào trường này. +danh sách tab Không có tìm kiếm nào gần đây phát Vui lòng nhập giá trị hợp lệ. Giá trị hợp lệ gần nhất là . @@ -35,16 +45,25 @@ chân trang âm thanh Giá trị phải là hoặc muộn hơn. +nút cửa sổ bật lên Vui lòng chọn hộp kiểm này nếu bạn muốn tiếp tục. 1024 (Loại Trung bình) +nhóm radio trạng thái phim hiện tại Vui lòng nhập danh sách địa chỉ email được phân cách bằng dấu phẩy. +nhấp vào +đối tượng nội dung được đánh dấu liên kết Vui lòng nhập một giá trị hợp lệ. Trường không hoàn chỉnh hoặc có giá trị không hợp lệ. +bảng +ghi chú Vui lòng nhập URL. +bộ tính giờ +hộp kết hợp Giá trị phải nhỏ hơn hoặc bằng . định nghĩa +tiêu đề số giây còn lại của phim chuyển mm @@ -52,60 +71,89 @@ cụm từ Hãy kéo dài văn bản này thành ký tự trở lên (bạn hiện đang sử dụng ký tự). Phần đứng trước '' không được chứa biểu tượng ''. +ứng dụng +thanh cuộn Mili giây +hình ảnh Hiển thị tháng trước chuyển +công cụ chọn màu +thanh menu +thông tin nội dung Vui lòng nhập giá trị hợp lệ. Hai giá trị hợp lệ gần nhất là . +nút menu Chi tiết biểu mẫu +cây Phần đứng sau '' không được chứa biểu tượng ''. +bảng điều khiển tab video +search Vui lòng nhập một số. Đã chọn Vui lòng nhập phần đứng sau ''. '' không hoàn chỉnh. +chỉ báo tiến trình đánh dấu danh sách Hiển thị tháng tiếp theo +tiêu đề hàng Giá trị phải lớn hơn hoặc bằng . Vui lòng nhập phần đứng trước ''. '' không hoàn chỉnh. +Lưu hình dáng +mục dạng cây Vui lòng chọn một hoặc nhiều tệp. thoát khỏi chế độ toàn màn hình bỏ chọn +bộ chọn ngày bật tiếng +thời gian tạm dừng phát lại +nút Khác... Đặt lại 2048 (Cấp độ cao) Tuần -địa chỉ +nút quay tròn Năm +hộp danh sách bật tiếng bản âm thanh kích hoạt +thước đo núm dòng thời gian phim núm trình kiểm soát thời gian phim Hiển thị bảng lựa chọn tháng trình kiểm soát thời gian âm thanh +bộ chọn giờ vào chế độ toàn màn hình +nhật ký Xóa Tìm kiếm Gần đây Khác... điều khiển tăng/giảm +thước +chú giải công cụ Không thể tải plugin. tắt tiếng nhấn ẩn phụ đề chi tiết +thư mục +khung trích dẫn thời gian phim Vui lòng khớp với định dạng được yêu cầu. thời gian trôi qua Vui lòng chọn một mục trong danh sách. +tiêu đề cột Chọn tệp Khác... +tab Giờ thời gian còn lại ngừng hiển thị phụ đề chi tiết Nội dung HTML tạm dừng +tam giác hiển thị Vui lòng bao gồm '' trong địa chỉ email. '' bị thiếu ''. +bộ chia Không có tệp nào được chọn trường văn bản tìm kiếm bắt đầu hiển thị phụ đề chi tiết @@ -116,8 +164,11 @@ bản đồ hình ảnh Giá trị không hợp lệ. chọn +thanh công cụ tệp Vui lòng chọn một tệp. +nút radio +nút thả xuống Phút kiểm soát phương tiện danh sách định nghĩa @@ -127,10 +178,15 @@ bắt đầu phát lại chính Tháng +mở thời gian hiện tại bằng giây điều hướng hiển thị phụ đề chi tiết +tài liệu toán học Khác... +thông báo , bắt đầu vào +ô +menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_zh-CN.xtb b/chromium/content/app/strings/translations/content_strings_zh-CN.xtb index a24fbb8c047..a556c73f52a 100644 --- a/chromium/content/app/strings/translations/content_strings_zh-CN.xtb +++ b/chromium/content/app/strings/translations/content_strings_zh-CN.xtb @@ -2,22 +2,30 @@ 空白 +菜单项 将音轨设为静音 切换按钮 请输入电子邮件地址。 补充内容 +marquee +滑块 清除最近的搜索 +对话框 文章标记 横幅 区域 本月 选择文件 +忙碌状态指示 请输入有效的电子邮件地址。 其他... +提醒对话框 +树状网格 上午/下午 在远程设备上播放 选中 +大纲 状态 指定的值不得晚于 @@ -26,8 +34,10 @@ 请将该文本减少为 个字符或更少(您当前使用了 个字符)。 年第 复选框 +日期和时间选择器 请填写此字段。 +标签列表 最近未执行搜索 播放 请输入有效值。最接近的有效值为 @@ -35,16 +45,25 @@ 页脚 音频 指定的值不得早于 +弹出式按钮 如果要继续,请选中此框。 1024(中等强度) +单选按钮组 当前电影状态 请输入用逗号分隔的电子邮件地址的列表。 +点击 +对象 突出显示的内容 链接 请输入一个有效值。该字段不完整或存在无效日期。 +表格 +备注 请输入网址。 +计时器 +组合框 值必须小于或等于 定义 +标题 电影剩余时间(以秒为单位) 切换 @@ -52,60 +71,89 @@ 字词 请将该文本增加为 个字符或更多(您当前使用的是 个字符)。 ”前面的内容不应包含符号“”。 +应用 +滚动条 毫秒 +图形 显示上一个月 略过 +颜色选择器 +菜单栏 +内容信息 请输入有效值。两个最接近的有效值分别为 +菜单按钮 详细信息 表单 +tree ”后面的内容不应包含符号“”。 +标签面板 视频 +搜索 请输入一个数字。 选择了 请在“”后面输入内容。“”不完整。 +进度指示器 列表标记 显示下一个月 +行标题 值必须大于或等于 请在“”前面输入内容。“”不完整。 +保存 图表 +树状目录项 请选择一个或多个文件。 退出全屏模式 取消选中 +日期选择器 取消静音 +time 暂停播放 +按钮 其他... 重置 2048(高强度) -地址 +微调按钮 +列表框 取消对音轨静音 激活 +计量条 电影时间轴缩略图 电影时间进度条缩略图 显示月份选择面板 音频时间进度条 +时间选择器 进入全屏模式 +日志 清除 近期搜索 其他... 步进器 +标尺 +提示 无法加载插件。 静音 隐藏可选字幕 +目录 +引用标记 电影时间 请与所请求的格式保持一致。 已播放时间 请在列表中选择一项。 +列标题 选择文件 其他... +标签 小时 剩余时间 停止显示可选字幕 HTML 内容 暂停 +开合三角标记 请在电子邮件地址中包括“”。“”中缺少“”。 +分离器 未选择任何文件 搜索文本字段 开始显示可选字幕 @@ -116,8 +164,11 @@ 图片映射 值无效。 选中 +工具栏 个文件 请选择一个文件。 +单选按钮 +下拉式按钮 分钟 媒体控件 定义列表 @@ -127,10 +178,15 @@ 开始播放 主体内容 +打开 目前时间(以秒为单位) 导航 显示可选字幕 +文档 数学 其他... +提醒 ,从 开始 +单元格 +菜单 \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_zh-TW.xtb b/chromium/content/app/strings/translations/content_strings_zh-TW.xtb index 7b0b0ee069f..556872f3abd 100644 --- a/chromium/content/app/strings/translations/content_strings_zh-TW.xtb +++ b/chromium/content/app/strings/translations/content_strings_zh-TW.xtb @@ -2,22 +2,30 @@ 空白 +選單項目 將音軌設為靜音 切換按鈕 請輸入電子郵件地址。 補充 +marquee +滑桿 清除最近的搜尋紀錄 +對話方塊 文章 橫幅 區域 本月 選擇檔案 +忙碌狀態指標 請輸入電子郵件地址。 其他... +警示對話方塊 +樹狀目錄網格 AM/PM 在遠端裝置上播放 選取 +大綱 狀態 必須輸入 或之前的值。 @@ -26,8 +34,10 @@ 請將這段文字刪減至 個字元以下 (目前的字元數為 個)。 年,第 核取方塊 +日期和時間選擇器 請填寫這個欄位。 +分頁清單 沒有近期的搜尋 播放 請輸入有效值。最接近的有效值是 @@ -35,16 +45,25 @@ 頁尾 音訊 必須輸入 或之後的值。 +彈出式按鈕 如果您要繼續執行,請勾選這個核取方塊。 1024 (中等) +圓形按鈕群組 目前電影狀態 請輸入以逗號分隔的電子郵件地址清單。 +點選 +物件 醒目顯示的內容 連結 欄位內容不完整或日期無效,請輸入有效的值。 +表格 +附註 請輸入網址。 +計時器 +下拉式方塊 值必須小於或等於 定義 +標題 電影剩餘秒數 切換 @@ -52,60 +71,89 @@ 字詞 請將這段文字加長到 個字元以上 (目前使用字元數:)。 」後面的部分不應包含「」符號。 +應用程式 +捲軸 毫秒 +圖形 顯示上一個月 跳至另一頁 +顏色選擇器 +選單列 +內容資訊 請輸入有效值。最接近的兩個有效值分別是 +選單按鈕 詳細資訊 表單 +tree 」後面的部分不應包含「」符號。 +分頁面板 影片 +search 請輸入一個數字。 已選取 個項目 請輸入「」後面的部分。「」不是完整值。 +進度指標 清單標記 顯示下一個月 +列標題 值必須大於或等於 請輸入「」後面的部分。「」不是完整值。 +儲存 圖表 +樹狀目錄項目 請選取一或多個檔案。 退出全螢幕 取消選取 +日期選擇器 取消靜音 +time 暫停播放 +按鈕 其他... 重設 2048 (高級) -地址 +微調按鈕 +清單方塊 取消音軌靜音 啟動 +計量器 電影時間軸捲動方塊 影片時間拖曳工具捲動方塊 顯示月份選取面板 音訊時間點拖曳工具 +時間選擇器 進入全螢幕 +紀錄 清除 最近的搜尋 其他... 步進器 +尺規 +工具提示 無法載入外掛程式。 忽略的項目 按下 不顯示字幕 +目錄 +引用標記 電影時間 請符合要求的格式。 已播放時間 請選取一個清單中的項目。 +欄標題 選擇檔案 其他... +分頁 小時 剩餘時間 停止顯示字幕 HTML 內容 暫停 +顯示/隱藏三角標記 請在電子郵件地址中包含「」。「」未包含「」。 +分割器 未選擇任何檔案 搜尋文字欄位 開始顯示字幕 @@ -116,8 +164,11 @@ 圖片點擊區 無效的值。 選取 +工具列 個檔案 請選取檔案。 +圓形按鈕 +下拉式清單按鈕 分鐘 媒體控制 定義清單 @@ -127,10 +178,15 @@ 開始播放 主要元素 +開啟 目前時間 (以秒為單位) 導覽 顯示字幕 +文件 數學 其他... +警示 ,從 開始 +儲存格 +選單 \ No newline at end of file diff --git a/chromium/content/browser/BUILD.gn b/chromium/content/browser/BUILD.gn index 4d66f86c09f..69f627a723b 100644 --- a/chromium/content/browser/BUILD.gn +++ b/chromium/content/browser/BUILD.gn @@ -10,150 +10,135 @@ import("//media/media_options.gni") source_set("browser") { # Only the public target should depend on this. All other targets (even # internal content ones) should depend on the public one. - visibility = [ "//content/public/browser:browser_sources" ] + visibility = [ + ":for_content_tests", # See top of //content/BUILD.gn for why. + "//content/public/browser:browser_sources", + ] configs += [ "//build/config:precompiled_headers", + "//content:content_implementation", "//content/public/common:mojo_shell_client", + "//third_party/WebKit/public:debug_devtools", + "//v8:external_startup_data", ] defines = [] libs = [] ldflags = [] - # Shared deps. See also non-iOS deps below. deps = [ "//base", "//base:base_static", + "//base/third_party/dynamic_annotations", + "//cc", + "//cc/surfaces", + "//components/filesystem:lib", + "//components/leveldb:lib", "//components/mime_util", + "//components/profile_service:lib", + "//components/scheduler:common", + "//components/tracing", + "//components/tracing:startup_tracing", "//components/url_formatter", "//content:resources", + "//content/app/resources", + "//content/app/strings", "//content/browser/background_sync:background_sync_proto", "//content/browser/cache_storage:cache_storage_proto", + "//content/browser/devtools:gen_devtools_protocol_handler", + "//content/browser/devtools:resources", "//content/browser/notifications:notification_proto", "//content/browser/service_worker:service_worker_proto", "//content/browser/speech/proto", + "//content/common", "//content/public/common:common_sources", "//content/public/common:mojo_bindings", "//crypto", "//device/battery", + "//device/bluetooth", "//device/vibration", + "//gin", "//google_apis", + "//gpu", + "//gpu/command_buffer/client:gles2_implementation", + "//ipc/mojo", + "//media", + "//media/midi", "//mojo/common", + "//mojo/common:url_type_converters", + "//mojo/converters/geometry", "//mojo/public/cpp/bindings", + "//mojo/public/js", "//mojo/shell", - "//mojo/shell/package_manager", "//mojo/shell/public/cpp:cpp_for_chromium", "//mojo/shell/public/interfaces", + "//mojo/shell/runner/common", + "//mojo/shell/runner/host:lib", "//net", "//net:extras", "//skia", + "//skia/public", "//sql", + "//storage/browser", + "//storage/common", + + # TODO(brettw) bug 582206: Blink should not be used in the browser + # process. This is required by devtools' input_handler.cc which calls + # WebKeyboardEvent::setKeyIdentifierFromWindowsKeyCode + "//third_party/WebKit/public:blink", "//third_party/WebKit/public:blink_headers", + "//third_party/WebKit/public:image_resources", + "//third_party/WebKit/public:resources", + "//third_party/angle:commit_id", + "//third_party/icu", "//third_party/kasko:kasko_features", - "//third_party/npapi", + "//third_party/leveldatabase", + "//third_party/libyuv", "//third_party/re2", + "//third_party/webrtc", + "//third_party/webrtc/base:rtc_base", + "//third_party/webrtc/modules/desktop_capture:primitives", "//third_party/zlib", "//third_party/zlib:zip", "//ui/accessibility", "//ui/accessibility:ax_gen", "//ui/base", "//ui/base/ime", + "//ui/display", "//ui/events", "//ui/events:gesture_detection", + "//ui/events/blink", "//ui/gfx", "//ui/gfx/geometry", "//ui/gl", "//ui/native_theme", "//ui/resources", + "//ui/shell_dialogs", "//ui/snapshot", + "//ui/surface", + "//ui/touch_selection", ] - if (is_ios) { - # iOS doesn't get the normal file list and only takes these whitelisted - # files. - sources = [ - "browser_context.cc", - "browser_main_loop.cc", - "browser_main_runner.cc", - "browser_process_sub_thread.cc", - "browser_thread_impl.cc", - "browser_url_handler_impl.cc", - "cert_store_impl.cc", - "download/download_create_info.cc", - "notification_service_impl.cc", - "signed_certificate_timestamp_store_impl.cc", - "user_metrics.cc", - "web_contents/navigation_entry_impl.cc", - ] - } else { - # Normal non-iOS sources get everything. - sources = rebase_path(content_browser_gypi_values.private_browser_sources, - ".", - "//content") - - # TODO(GYP) these generated files are listed as sources in content_browser. - # This is a bit suspicious. The GN grit template will make a source set - # containing the generated code so it should be sufficient to just depend - # on the grit rule. But maybe some of these will need to be added? - # - # Need this annoying rebase_path call to match what happened with the - # sources. - sources -= rebase_path( - [ - "$root_gen_dir/blink/grit/devtools_resources.h", - "$root_gen_dir/blink/grit/devtools_resources_map.cc", - "$root_gen_dir/blink/grit/devtools_resources_map.h", - "$root_gen_dir/content/browser/tracing/grit/tracing_resources.h", - "$root_gen_dir/ui/resources/grit/webui_resources_map.cc", - ], - ".") - - sources += [ - "mojo/mojo_shell_client_host.cc", - "mojo/mojo_shell_client_host.h", - "mojo/renderer_capability_filter.cc", - ] - - # Non-iOS deps. - deps += [ - "//cc", - "//cc/surfaces", - "//components/scheduler:common", - "//content/app/resources", - "//content/app/strings", - "//content/browser/devtools:gen_devtools_protocol_handler", - "//content/browser/devtools:resources", - "//content/common:mojo_bindings", - "//content/public/common:mojo_bindings", - "//device/bluetooth", - "//gin", - "//mojo/common:url_type_converters", - "//mojo/converters/geometry", - "//mojo/public/cpp/bindings", - "//mojo/public/js", - "//mojo/shell/public/interfaces", - "//skia/public", - "//storage/browser", - "//storage/common", - "//third_party/WebKit/public:image_resources", - "//third_party/WebKit/public:resources", - "//third_party/angle:commit_id", - "//third_party/icu", - "//third_party/leveldatabase", - "//third_party/libyuv", - "//ui/events/blink", - "//ui/resources", - "//ui/surface", - "//ui/touch_selection", - ] - - configs += [ "//v8:external_startup_data" ] - } - - configs += [ - "//content:content_implementation", - "//third_party/WebKit/public:debug_devtools", - ] + sources = rebase_path(content_browser_gypi_values.private_browser_sources, + ".", + "//content") + + # TODO(GYP) these generated files are listed as sources in content_browser. + # This is a bit suspicious. The GN grit template will make a source set + # containing the generated code so it should be sufficient to just depend + # on the grit rule. But maybe some of these will need to be added? + # + # Need this annoying rebase_path call to match what happened with the + # sources. + sources -= rebase_path( + [ + "$root_gen_dir/blink/grit/devtools_resources.h", + "$root_gen_dir/blink/grit/devtools_resources_map.cc", + "$root_gen_dir/blink/grit/devtools_resources_map.h", + "$root_gen_dir/content/browser/tracing/grit/tracing_resources.h", + "$root_gen_dir/ui/resources/grit/webui_resources_map.cc", + ], + ".") if (toolkit_views) { deps += [ "//ui/events" ] @@ -176,32 +161,37 @@ source_set("browser") { } # TODO(GYP) - # ['OS!="ios" and chrome_multiple_dll!=1', { + # [chrome_multiple_dll!=1', { # 'dependencies': [ # '../third_party/WebKit/public/blink.gyp:blink', # ], # }], - if (!is_mac && !is_ios) { + if (!is_mac) { deps += [ "//sandbox" ] } - if (!is_android && !is_ios) { + if (!is_android) { deps += [ "//content/browser/tracing:resources" ] } + if ((use_udev && is_posix) || is_mac || is_win) { + deps += [ "//tools/battor_agent:battor_agent_lib" ] + sources += [ + "tracing/power_tracing_agent.cc", + "tracing/power_tracing_agent.h", + ] + } if (enable_webrtc) { sources += rebase_path(content_browser_gypi_values.webrtc_browser_sources, ".", "//content") - deps += [ "//jingle:jingle_glue" ] - if (is_linux) { - deps += [ "//third_party/libjingle:libjingle_webrtc" ] - } + deps += [ + "//jingle:jingle_glue", + "//third_party/libjingle:libjingle_webrtc", + ] if (is_linux || is_mac || is_win) { sources += [ "media/capture/desktop_capture_device.cc", "media/capture/desktop_capture_device.h", - "media/capture/desktop_capture_device_uma_types.cc", - "media/capture/desktop_capture_device_uma_types.h", ] if (use_aura) { sources += [ @@ -235,6 +225,7 @@ source_set("browser") { "dinput8.lib", "dwmapi.lib", "dxguid.lib", + "imm32.lib", "oleacc.lib", "sensorsapi.lib", "portabledeviceguids.lib", @@ -251,20 +242,13 @@ source_set("browser") { } if (use_udev) { - deps += [ "//device/udev_linux" ] - } else { - # Remove udev-specific sources. - sources -= [ - "device_monitor_udev.cc", - "device_monitor_udev.h", + deps += [ + "//device/udev_linux", + "//media/capture", ] + } else { if (is_linux) { - # Already filtered out on non-Linux. - sources -= [ - "gamepad/gamepad_platform_data_fetcher_linux.cc", - "udev_linux.cc", - "udev_linux.h", - ] + sources -= [ "gamepad/gamepad_platform_data_fetcher_linux.cc" ] } } @@ -305,6 +289,9 @@ source_set("browser") { if (use_x11) { configs += [ "//build/config/linux:x11" ] + if (!is_chromeos) { + configs += [ "//build/config/linux:xscrnsaver" ] + } deps += [ "//ui/gfx/x" ] } @@ -338,6 +325,10 @@ source_set("browser") { sources += rebase_path(content_browser_gypi_values.android_browser_sources, ".", "//content") + sources += rebase_path( + content_browser_gypi_values.android_in_process_browser_sources, + ".", + "//content") sources -= [ "browser_ipc_logging.cc", "device_sensors/data_fetcher_shared_memory_default.cc", @@ -345,8 +336,10 @@ source_set("browser") { "geolocation/network_location_provider.h", "geolocation/network_location_request.cc", "geolocation/network_location_request.h", + "media/session/media_session_delegate_default.cc", "power_usage_monitor_impl.cc", "power_usage_monitor_impl.h", + "renderer_host/begin_frame_observer_proxy.cc", "tracing/tracing_ui.cc", "tracing/tracing_ui.h", @@ -372,26 +365,13 @@ source_set("browser") { "speech/speech_recognizer_impl.cc", "speech/speech_recognizer_impl.h", ] - - if (!use_aura) { - sources += rebase_path( - content_browser_gypi_values.android_non_aura_browser_sources, - ".", - "//content") - sources += rebase_path( - content_browser_gypi_values.android_in_process_browser_sources, - ".", - "//content") - sources -= [ "renderer_host/begin_frame_observer_proxy.cc" ] - deps += [ "//ui/android" ] - } - deps -= [ "//device/battery" ] deps += [ "//content/public/android:jni", "//media", "//media/mojo/interfaces", "//mojo/android:libsystem_java", + "//ui/android", ] defines += [ "APPCACHE_USE_SIMPLE_CACHE" ] libs += [ "jnigraphics" ] @@ -409,18 +389,10 @@ source_set("browser") { "//third_party/sudden_motion_sensor", "//ui/accelerated_widget_mac", ] - libs += [ - "bsm", - "QTKit.framework", - ] } if (is_chromeos) { sources -= [ "device_sensors/data_fetcher_shared_memory_default.cc" ] - sources += [ - "gpu/gpu_arc_video_service_host.cc", - "gpu/gpu_arc_video_service_host.h", - ] deps += [ "//chromeos", "//chromeos:power_manager_proto", @@ -506,13 +478,6 @@ source_set("browser") { deps += [ "//ui/compositor" ] } - if (!is_ios) { - sources += [ - "compositor/surface_utils.cc", - "compositor/surface_utils.h", - ] - } - if (enable_web_speech) { deps += [ "//third_party/flac" ] } @@ -532,12 +497,8 @@ source_set("browser") { deps += [ "//third_party/boringssl" ] } - if (enable_mojo_media != "none") { - configs += [ "//media/mojo/services:enable_mojo_media_config" ] - } - - if (enable_mojo_media == "browser") { - deps += [ "//media/mojo/services:application" ] + if (enable_mojo_media) { + configs += [ "//media/mojo/services:mojo_media_config" ] } if (enable_webvr) { @@ -559,3 +520,13 @@ source_set("browser") { ] } } + +# See comment at the top of //content/BUILD.gn for how this works. +group("for_content_tests") { + visibility = [ "//content/test/*" ] + if (!is_component_build) { + public_deps = [ + ":browser", + ] + } +} diff --git a/chromium/content/browser/DEPS b/chromium/content/browser/DEPS index 5e33c93e2de..42e02ee046e 100644 --- a/chromium/content/browser/DEPS +++ b/chromium/content/browser/DEPS @@ -1,10 +1,12 @@ include_rules = [ # Allow inclusion of specific components that we depend on. We may only # depend on components which we share with the mojo html_viewer. - "+components/arc", + "+components/filesystem", + "+components/leveldb", "+components/mime_util", "+components/mus/public/interfaces", "+components/mus/public", + "+components/profile_service", "+components/scheduler/common", "+components/tracing", "+components/url_formatter", @@ -16,11 +18,12 @@ include_rules = [ "+gin/v8_initializer.h", "+media/media_features.h", "+media/audio", # For audio input for speech input feature. + "+media/capture", # For Video Device monitoring in Mac. "+media/base", # For Android JNI registration. + "+media/capture", # For Device Monitoring "+media/filters", # For reporting GPU decoding UMA. "+media/midi", # For Web MIDI API "+media/mojo", # For mojo media services. - "+media/capture/video", # For Video Device monitoring in Mac. "+mojo", "+sql", "+ui/aura_extra", @@ -49,6 +52,7 @@ include_rules = [ # No inclusion of WebKit from the browser, other than strictly enum/POD, # header-only types, and some selected common code. "-third_party/WebKit", + "+third_party/WebKit/public/platform/WebAddressSpace.h", "+third_party/WebKit/public/platform/WebCircularGeofencingRegion.h", "+third_party/WebKit/public/platform/WebCursorInfo.h", "+third_party/WebKit/public/platform/WebDisplayMode.h", @@ -61,9 +65,12 @@ include_rules = [ "+third_party/WebKit/public/platform/WebReferrerPolicy.h", "+third_party/WebKit/public/platform/WebScreenInfo.h", "+third_party/WebKit/public/platform/WebString.h", + "+third_party/WebKit/public/platform/modules/geolocation/geolocation.mojom.h", "+third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h", "+third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h", "+third_party/WebKit/public/platform/modules/notifications/WebNotificationPermission.h", + "+third_party/WebKit/public/platform/modules/permissions/permission.mojom.h", + "+third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h", "+third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h", "+third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h", "+third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationType.h", @@ -96,6 +103,10 @@ include_rules = [ "+third_party/WebKit/public/web/WebTreeScopeType.h", "+third_party/WebKit/public/web/mac/WebScrollbarTheme.h", + # Until we define where mojo interfaces should live in blink we whitelist each + # one separately. + "+third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom.h" + # DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!! # See https://sites.google.com/a/chromium.org/dev/developers/content-module # for more information. diff --git a/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc b/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc index fa767494fb4..cb40bfbb850 100644 --- a/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc +++ b/chromium/content/browser/accessibility/accessibility_event_recorder_win.cc @@ -287,7 +287,7 @@ HRESULT AccessibilityEventRecorderWin::AccessibleObjectFromWindowWrapper( if (accessibility_hwnd != hwnd) return E_FAIL; - IAccessible* obj = manager_->GetRoot()->ToBrowserAccessibilityWin(); + IAccessible* obj = ToBrowserAccessibilityWin(manager_->GetRoot()); obj->AddRef(); *ppv_object = obj; return S_OK; diff --git a/chromium/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/chromium/content/browser/accessibility/accessibility_ipc_error_browsertest.cc index a8bd5412df5..597631cc221 100644 --- a/chromium/content/browser/accessibility/accessibility_ipc_error_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_ipc_error_browsertest.cc @@ -132,7 +132,6 @@ IN_PROC_BROWSER_TEST_F(AccessibilityIpcErrorBrowserTest, const ui::AXNode* button = button_container->ChildAtIndex(0); EXPECT_EQ(ui::AX_ROLE_BUTTON, button->data().role); - EXPECT_TRUE(button->data().state >> ui::AX_STATE_FOCUSED & 1); } #if defined(OS_ANDROID) diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc index 12321df619d..24fce4d93ce 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_android.cc @@ -127,6 +127,7 @@ void AccessibilityTreeFormatterAndroid::AddProperties( // String attributes. dict->SetString("name", android_node->GetText()); + dict->SetString("role_description", android_node->GetRoleDescription()); // Int attributes. dict->SetInteger("item_index", android_node->GetItemIndex()); @@ -171,6 +172,15 @@ base::string16 AccessibilityTreeFormatterAndroid::ToString( dict.GetString("class", &class_value); WriteAttribute(true, base::UTF16ToUTF8(class_value), &line); + std::string role_description; + dict.GetString("role_description", &role_description); + if (!role_description.empty()) { + WriteAttribute( + true, + StringPrintf("role_description='%s'", role_description.c_str()), + &line); + } + for (unsigned i = 0; i < arraysize(BOOL_ATTRIBUTES); i++) { const char* attribute_name = BOOL_ATTRIBUTES[i]; bool value; diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc index 4ebb1c0e46d..ef954c9e26c 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc @@ -46,8 +46,7 @@ void AccessibilityTreeFormatterAuraLinux::AddProperties( base::DictionaryValue* dict) { dict->SetInteger("id", node.GetId()); BrowserAccessibilityAuraLinux* acc_obj = - const_cast(&node) - ->ToBrowserAccessibilityAuraLinux(); + ToBrowserAccessibilityAuraLinux(const_cast(&node)); AtkObject* atk_object = acc_obj->GetAtkObject(); AtkRole role = acc_obj->atk_role(); diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc index 27b9c9fa1cd..6796770f2a5 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc @@ -7,6 +7,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" +#include "ui/gfx/transform.h" namespace content { @@ -19,13 +20,19 @@ AccessibilityTreeFormatterBlink::~AccessibilityTreeFormatterBlink() { uint32_t AccessibilityTreeFormatterBlink::ChildCount( const BrowserAccessibility& node) const { - return node.InternalChildCount(); + if (node.HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) + return node.PlatformChildCount(); + else + return node.InternalChildCount(); } BrowserAccessibility* AccessibilityTreeFormatterBlink::GetChild( const BrowserAccessibility& node, uint32_t i) const { - return node.InternalGetChild(i); + if (node.HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) + return node.PlatformGetChild(i); + else + return node.InternalGetChild(i); } void AccessibilityTreeFormatterBlink::AddProperties( @@ -33,12 +40,23 @@ void AccessibilityTreeFormatterBlink::AddProperties( base::DictionaryValue* dict) { dict->SetInteger("id", node.GetId()); - dict->SetString("role", ui::ToString(node.GetData().role)); + dict->SetString("internalRole", ui::ToString(node.GetData().role)); - dict->SetInteger("boundsX", node.GetData().location.x()); - dict->SetInteger("boundsY", node.GetData().location.y()); - dict->SetInteger("boundsWidth", node.GetData().location.width()); - dict->SetInteger("boundsHeight", node.GetData().location.height()); + gfx::Rect bounds = node.GetData().location; + dict->SetInteger("boundsX", bounds.x()); + dict->SetInteger("boundsY", bounds.y()); + dict->SetInteger("boundsWidth", bounds.width()); + dict->SetInteger("boundsHeight", bounds.height()); + + gfx::Rect page_bounds = node.GetLocalBoundsRect(); + dict->SetInteger("pageBoundsX", page_bounds.x()); + dict->SetInteger("pageBoundsY", page_bounds.y()); + dict->SetInteger("pageBoundsWidth", page_bounds.width()); + dict->SetInteger("pageBoundsHeight", page_bounds.height()); + + dict->SetBoolean("transform", + node.GetData().transform && + !node.GetData().transform->IsIdentity()); for (int state_index = ui::AX_STATE_NONE; state_index <= ui::AX_STATE_LAST; @@ -106,7 +124,7 @@ base::string16 AccessibilityTreeFormatterBlink::ToString( } base::string16 role_value; - dict.GetString("role", &role_value); + dict.GetString("internalRole", &role_value); WriteAttribute(true, base::UTF16ToUTF8(role_value), &line); for (int state_index = ui::AX_STATE_NONE; @@ -127,6 +145,19 @@ base::string16 AccessibilityTreeFormatterBlink::ToString( FormatCoordinates("size", "boundsWidth", "boundsHeight", dict), &line); + WriteAttribute(false, + FormatCoordinates("pageLocation", + "pageBoundsX", "pageBoundsY", dict), + &line); + WriteAttribute(false, + FormatCoordinates("pageSize", + "pageBoundsWidth", "pageBoundsHeight", dict), + &line); + + bool transform; + if (dict.GetBoolean("transform", &transform) && transform) + WriteAttribute(false, "transform", &line); + for (int attr_index = ui::AX_STRING_ATTRIBUTE_NONE; attr_index <= ui::AX_STRING_ATTRIBUTE_LAST; ++attr_index) { diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm index 0721a9e991f..48b9d883167 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_mac.mm @@ -41,15 +41,15 @@ scoped_ptr PopulatePosition( // based on the lower-left corner of the object. To make this easier and less // confusing, convert it to local window coordinates using the top-left // corner when dumping the position. - BrowserAccessibility* root = node.manager()->GetRoot(); - BrowserAccessibilityCocoa* cocoa_root = root->ToBrowserAccessibilityCocoa(); + BrowserAccessibility* root = node.manager()->GetRootManager()->GetRoot(); + BrowserAccessibilityCocoa* cocoa_root = ToBrowserAccessibilityCocoa(root); NSPoint root_position = [[cocoa_root position] pointValue]; NSSize root_size = [[cocoa_root size] sizeValue]; int root_top = -static_cast(root_position.y + root_size.height); int root_left = static_cast(root_position.x); BrowserAccessibilityCocoa* cocoa_node = - const_cast(&node)->ToBrowserAccessibilityCocoa(); + ToBrowserAccessibilityCocoa(const_cast(&node)); NSPoint node_position = [[cocoa_node position] pointValue]; NSSize node_size = [[cocoa_node size] sizeValue]; @@ -221,7 +221,7 @@ void AccessibilityTreeFormatterMac::AddProperties( base::DictionaryValue* dict) { dict->SetInteger("id", node.GetId()); BrowserAccessibilityCocoa* cocoa_node = - const_cast(&node)->ToBrowserAccessibilityCocoa(); + ToBrowserAccessibilityCocoa(const_cast(&node)); NSArray* supportedAttributes = [cocoa_node accessibilityAttributeNames]; string role = SysNSStringToUTF8( diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc index cbece3a2553..eaf4fc8f4aa 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_win.cc @@ -54,36 +54,35 @@ AccessibilityTreeFormatterWin::AccessibilityTreeFormatterWin() { AccessibilityTreeFormatterWin::~AccessibilityTreeFormatterWin() { } -const char* ALL_ATTRIBUTES[] = { - "name", - "value", - "states", - "attributes", - "role_name", - "ia2_hypertext", - "currentValue", - "minimumValue", - "maximumValue", - "description", - "default_action", - "keyboard_shortcut", - "location", - "size", - "index_in_parent", - "n_relations", - "group_level", - "similar_items_in_group", - "position_in_group", - "table_rows", - "table_columns", - "row_index", - "column_index", - "n_characters", - "caret_offset", - "n_selections", - "selection_start", - "selection_end" -}; +const char* ALL_ATTRIBUTES[] = {"name", + "value", + "states", + "attributes", + "text_attributes", + "role_name", + "ia2_hypertext", + "currentValue", + "minimumValue", + "maximumValue", + "description", + "default_action", + "keyboard_shortcut", + "location", + "size", + "index_in_parent", + "n_relations", + "group_level", + "similar_items_in_group", + "position_in_group", + "table_rows", + "table_columns", + "row_index", + "column_index", + "n_characters", + "caret_offset", + "n_selections", + "selection_start", + "selection_end"}; namespace { @@ -157,7 +156,7 @@ void AccessibilityTreeFormatterWin::AddProperties( const BrowserAccessibility& node, base::DictionaryValue* dict) { dict->SetInteger("id", node.GetId()); BrowserAccessibilityWin* ax_object = - const_cast(&node)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(const_cast(&node)); DCHECK(ax_object); VARIANT variant_self; @@ -167,8 +166,13 @@ void AccessibilityTreeFormatterWin::AddProperties( dict->SetString("role", IAccessible2RoleToString(ax_object->ia2_role())); base::win::ScopedBstr temp_bstr; - if (SUCCEEDED(ax_object->get_accName(variant_self, temp_bstr.Receive()))) - dict->SetString("name", base::string16(temp_bstr, temp_bstr.Length())); + if (SUCCEEDED(ax_object->get_accName(variant_self, temp_bstr.Receive()))) { + base::string16 name = base::string16(temp_bstr, temp_bstr.Length()); + + // Ignore a JAWS workaround where the name of a document is " ". + if (name != L" " || ax_object->ia2_role() != ROLE_SYSTEM_DOCUMENT) + dict->SetString("name", name); + } temp_bstr.Reset(); if (SUCCEEDED(ax_object->get_accValue(variant_self, temp_bstr.Receive()))) @@ -185,17 +189,29 @@ void AccessibilityTreeFormatterWin::AddProperties( IAccessibleStateToStringVector(ia_state, &state_strings); IAccessible2StateToStringVector(ax_object->ia2_state(), &state_strings); - base::ListValue* states = new base::ListValue; - for (const auto& state_string : state_strings) + scoped_ptr states(new base::ListValue()); + for (const base::string16& state_string : state_strings) states->AppendString(base::UTF16ToUTF8(state_string)); - dict->Set("states", states); + dict->Set("states", std::move(states)); const std::vector& ia2_attributes = ax_object->ia2_attributes(); - base::ListValue* attributes = new base::ListValue; - for (const auto& ia2_attribute : ia2_attributes) + scoped_ptr attributes(new base::ListValue()); + for (const base::string16& ia2_attribute : ia2_attributes) attributes->AppendString(base::UTF16ToUTF8(ia2_attribute)); - dict->Set("attributes", attributes); + dict->Set("attributes", std::move(attributes)); + + ax_object->ComputeStylesIfNeeded(); + const std::map>& ia2_text_attributes = + ax_object->offset_to_text_attributes(); + scoped_ptr text_attributes(new base::ListValue()); + for (const auto& style_span : ia2_text_attributes) { + int start_offset = style_span.first; + text_attributes->AppendString("offset:" + base::IntToString(start_offset)); + for (const base::string16& text_attribute : style_span.second) + text_attributes->AppendString(base::UTF16ToUTF8(text_attribute)); + } + dict->Set("text_attributes", std::move(text_attributes)); dict->SetString("role_name", ax_object->role_name()); dict->SetString("ia2_hypertext", GetIA2Hypertext(*ax_object)); @@ -237,12 +253,12 @@ void AccessibilityTreeFormatterWin::AddProperties( dict->SetString("help", base::string16(temp_bstr, temp_bstr.Length())); temp_bstr.Reset(); - BrowserAccessibility* root = node.manager()->GetRoot(); + BrowserAccessibility* root = node.manager()->GetRootManager()->GetRoot(); LONG left, top, width, height; LONG root_left, root_top, root_width, root_height; if (SUCCEEDED(ax_object->accLocation( &left, &top, &width, &height, variant_self)) && - SUCCEEDED(root->ToBrowserAccessibilityWin()->accLocation( + SUCCEEDED(ToBrowserAccessibilityWin(root)->accLocation( &root_left, &root_top, &root_width, &root_height, variant_self))) { base::DictionaryValue* location = new base::DictionaryValue; location->SetInteger("x", left - root_left); @@ -340,7 +356,7 @@ base::string16 AccessibilityTreeFormatterWin::ToString( break; } case base::Value::TYPE_INTEGER: { - int int_value; + int int_value = 0; value->GetAsInteger(&int_value); WriteAttribute(false, base::StringPrintf(L"%ls=%d", @@ -351,7 +367,7 @@ base::string16 AccessibilityTreeFormatterWin::ToString( break; } case base::Value::TYPE_DOUBLE: { - double double_value; + double double_value = 0.0; value->GetAsDouble(&double_value); WriteAttribute(false, base::StringPrintf(L"%ls=%.2f", diff --git a/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc b/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc deleted file mode 100644 index 12817d10b0c..00000000000 --- a/chromium/content/browser/accessibility/android_hit_testing_browsertest.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/strings/string16.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "content/browser/accessibility/accessibility_tree_formatter.h" -#include "content/browser/accessibility/browser_accessibility.h" -#include "content/browser/accessibility/browser_accessibility_manager.h" -#include "content/browser/web_contents/web_contents_impl.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/content_paths.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/url_constants.h" -#include "content/public/test/content_browser_test.h" -#include "content/public/test/content_browser_test_utils.h" -#include "content/shell/browser/shell.h" -#include "content/test/accessibility_browser_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -class AndroidHitTestingBrowserTest : public ContentBrowserTest { - public: - AndroidHitTestingBrowserTest() {} - ~AndroidHitTestingBrowserTest() override {} -}; - -IN_PROC_BROWSER_TEST_F(AndroidHitTestingBrowserTest, - HitTestOutsideDocumentBoundsReturnsRoot) { - NavigateToURL(shell(), GURL(url::kAboutBlankURL)); - - // Load the page. - AccessibilityNotificationWaiter waiter( - shell(), AccessibilityModeComplete, - ui::AX_EVENT_LOAD_COMPLETE); - const char url_str[] = - "data:text/html," - "" - "Accessibility Test" - "" - "" - "This is some text in a link" - "" - ""; - GURL url(url_str); - NavigateToURL(shell(), url); - waiter.WaitForNotification(); - - // Get the BrowserAccessibilityManager. - WebContentsImpl* web_contents = - static_cast(shell()->web_contents()); - BrowserAccessibilityManager* manager = - web_contents->GetRootBrowserAccessibilityManager(); - - // Send a hit test request, and wait for the hover event in response. - AccessibilityNotificationWaiter hover_waiter( - shell(), AccessibilityModeComplete, - ui::AX_EVENT_HOVER); - manager->delegate()->AccessibilityHitTest(gfx::Point(-1, -1)); - hover_waiter.WaitForNotification(); - - // Assert that the hover event was fired on the root of the tree. - int hover_target_id = hover_waiter.event_target_id(); - BrowserAccessibility* hovered_node = manager->GetFromID(hover_target_id); - ASSERT_TRUE(hovered_node != NULL); - ASSERT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, hovered_node->GetRole()); -} - -} // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index 3c43e153fe6..fa606fc2bfe 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -15,9 +15,19 @@ #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/common/accessibility_messages.h" #include "ui/accessibility/ax_text_utils.h" +#include "ui/accessibility/platform/ax_platform_node.h" +#include "ui/gfx/geometry/rect_f.h" namespace content { +namespace { + +// Map from unique_id to BrowserAccessibility +using UniqueIDMap = base::hash_map; +base::LazyInstance g_unique_id_map = LAZY_INSTANCE_INITIALIZER; + +} + #if !defined(PLATFORM_HAS_NATIVE_ACCESSIBILITY_IMPL) // static BrowserAccessibility* BrowserAccessibility::Create() { @@ -27,10 +37,23 @@ BrowserAccessibility* BrowserAccessibility::Create() { BrowserAccessibility::BrowserAccessibility() : manager_(NULL), - node_(NULL) { + node_(NULL), + unique_id_(ui::AXPlatformNode::GetNextUniqueId()) { + g_unique_id_map.Get()[unique_id_] = this; } BrowserAccessibility::~BrowserAccessibility() { + if (unique_id_) + g_unique_id_map.Get().erase(unique_id_); +} + +// static +BrowserAccessibility* BrowserAccessibility::GetFromUniqueID(int32_t unique_id) { + auto iter = g_unique_id_map.Get().find(unique_id); + if (iter == g_unique_id_map.Get().end()) + return nullptr; + + return iter->second; } void BrowserAccessibility::Init(BrowserAccessibilityManager* manager, @@ -73,7 +96,7 @@ uint32_t BrowserAccessibility::PlatformChildCount() const { BrowserAccessibilityManager* child_manager = BrowserAccessibilityManager::FromID( GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); - if (child_manager) + if (child_manager && child_manager->GetRoot()->GetParent() == this) return 1; return 0; @@ -88,11 +111,14 @@ bool BrowserAccessibility::IsNative() const { bool BrowserAccessibility::IsDescendantOf( const BrowserAccessibility* ancestor) const { - if (this == ancestor) { + if (!ancestor) + return false; + + if (this == ancestor) return true; - } else if (GetParent()) { + + if (GetParent()) return GetParent()->IsDescendantOf(ancestor); - } return false; } @@ -112,7 +138,7 @@ BrowserAccessibility* BrowserAccessibility::PlatformGetChild( BrowserAccessibilityManager* child_manager = BrowserAccessibilityManager::FromID( GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); - if (child_manager) + if (child_manager && child_manager->GetRoot()->GetParent() == this) result = child_manager->GetRoot(); } else { result = InternalGetChild(child_index); @@ -132,6 +158,16 @@ bool BrowserAccessibility::PlatformIsChildOfLeaf() const { return false; } +BrowserAccessibility* BrowserAccessibility::GetClosestPlatformObject() const { + BrowserAccessibility* platform_object = + const_cast(this); + while (platform_object && platform_object->PlatformIsChildOfLeaf()) + platform_object = platform_object->InternalGetParent(); + + DCHECK(platform_object); + return platform_object; +} + BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() const { if (GetParent() && GetIndexInParent() > 0) return GetParent()->InternalGetChild(GetIndexInParent() - 1); @@ -154,7 +190,7 @@ BrowserAccessibility* BrowserAccessibility::PlatformDeepestFirstChild() const { if (!PlatformChildCount()) return nullptr; - auto deepest_child = PlatformGetChild(0); + BrowserAccessibility* deepest_child = PlatformGetChild(0); while (deepest_child->PlatformChildCount()) deepest_child = deepest_child->PlatformGetChild(0); @@ -165,7 +201,8 @@ BrowserAccessibility* BrowserAccessibility::PlatformDeepestLastChild() const { if (!PlatformChildCount()) return nullptr; - auto deepest_child = PlatformGetChild(PlatformChildCount() - 1); + BrowserAccessibility* deepest_child = + PlatformGetChild(PlatformChildCount() - 1); while (deepest_child->PlatformChildCount()) { deepest_child = deepest_child->PlatformGetChild( deepest_child->PlatformChildCount() - 1); @@ -174,6 +211,31 @@ BrowserAccessibility* BrowserAccessibility::PlatformDeepestLastChild() const { return deepest_child; } +BrowserAccessibility* BrowserAccessibility::InternalDeepestFirstChild() const { + if (!InternalChildCount()) + return nullptr; + + BrowserAccessibility* deepest_child = InternalGetChild(0); + while (deepest_child->InternalChildCount()) + deepest_child = deepest_child->InternalGetChild(0); + + return deepest_child; +} + +BrowserAccessibility* BrowserAccessibility::InternalDeepestLastChild() const { + if (!InternalChildCount()) + return nullptr; + + BrowserAccessibility* deepest_child = + InternalGetChild(InternalChildCount() - 1); + while (deepest_child->InternalChildCount()) { + deepest_child = deepest_child->InternalGetChild( + deepest_child->InternalChildCount() - 1); + } + + return deepest_child; +} + uint32_t BrowserAccessibility::InternalChildCount() const { if (!node_ || !manager_) return 0; @@ -191,8 +253,9 @@ BrowserAccessibility* BrowserAccessibility::InternalGetChild( } BrowserAccessibility* BrowserAccessibility::GetParent() const { - if (!node_ || !manager_) - return NULL; + if (!instance_active()) + return nullptr; + ui::AXNode* parent = node_->parent(); if (parent) return manager_->GetFromAXNode(parent); @@ -321,17 +384,25 @@ gfx::Rect BrowserAccessibility::GetLocalBoundsForRange(int start, int len) int local_start = overlap_start - child_start; int local_end = overlap_end - child_start; + // |local_end| and |local_start| may equal |child_length| when the caret is + // at the end of a text field. + DCHECK_GE(local_start, 0); + DCHECK_LE(local_start, child_length); + DCHECK_GE(local_end, 0); + DCHECK_LE(local_end, child_length); - gfx::Rect child_rect = child->GetLocation(); - int text_direction = child->GetIntAttribute( - ui::AX_ATTR_TEXT_DIRECTION); const std::vector& character_offsets = child->GetIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS); + if (static_cast(character_offsets.size()) != child_length) + continue; int start_pixel_offset = local_start > 0 ? character_offsets[local_start - 1] : 0; int end_pixel_offset = local_end > 0 ? character_offsets[local_end - 1] : 0; + gfx::Rect child_rect = child->GetLocation(); + auto text_direction = static_cast( + child->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)); gfx::Rect child_overlap_rect; switch (text_direction) { case ui::AX_TEXT_DIRECTION_NONE: @@ -389,7 +460,10 @@ gfx::Rect BrowserAccessibility::GetGlobalBoundsForRange(int start, int len) base::string16 BrowserAccessibility::GetValue() const { base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); - if (value.empty() && IsSimpleTextControl()) + // Some screen readers like Jaws and older versions of VoiceOver require a + // value to be set in text fields with rich content, even though the same + // information is available on the children. + if (value.empty() && (IsSimpleTextControl() || IsRichTextControl())) value = GetInnerText(); return value; } @@ -555,6 +629,10 @@ void BrowserAccessibility::Destroy() { node_ = NULL; manager_ = NULL; + if (unique_id_) + g_unique_id_map.Get().erase(unique_id_); + unique_id_ = 0; + NativeReleaseReference(); } @@ -592,6 +670,72 @@ bool BrowserAccessibility::GetFloatAttribute( return GetData().GetFloatAttribute(attribute, value); } +bool BrowserAccessibility::HasInheritedStringAttribute( + ui::AXStringAttribute attribute) const { + if (!instance_active()) + return false; + + if (GetData().HasStringAttribute(attribute)) + return true; + return GetParent() && GetParent()->HasInheritedStringAttribute(attribute); +} + +const std::string& BrowserAccessibility::GetInheritedStringAttribute( + ui::AXStringAttribute attribute) const { + if (!instance_active()) + return base::EmptyString(); + + const BrowserAccessibility* current_object = this; + do { + if (current_object->GetData().HasStringAttribute(attribute)) + return current_object->GetData().GetStringAttribute(attribute); + current_object = current_object->GetParent(); + } while (current_object); + return base::EmptyString(); +} + +bool BrowserAccessibility::GetInheritedStringAttribute( + ui::AXStringAttribute attribute, + std::string* value) const { + if (!instance_active()) { + *value = std::string(); + return false; + } + + if (GetData().GetStringAttribute(attribute, value)) + return true; + return GetParent() && + GetParent()->GetData().GetStringAttribute(attribute, value); +} + +base::string16 BrowserAccessibility::GetInheritedString16Attribute( + ui::AXStringAttribute attribute) const { + if (!instance_active()) + return base::string16(); + + const BrowserAccessibility* current_object = this; + do { + if (current_object->GetData().HasStringAttribute(attribute)) + return current_object->GetData().GetString16Attribute(attribute); + current_object = current_object->GetParent(); + } while (current_object); + return base::string16(); +} + +bool BrowserAccessibility::GetInheritedString16Attribute( + ui::AXStringAttribute attribute, + base::string16* value) const { + if (!instance_active()) { + *value = base::string16(); + return false; + } + + if (GetData().GetString16Attribute(attribute, value)) + return true; + return GetParent() && + GetParent()->GetData().GetString16Attribute(attribute, value); +} + bool BrowserAccessibility::HasIntAttribute( ui::AXIntAttribute attribute) const { return GetData().HasIntAttribute(attribute); @@ -626,9 +770,8 @@ base::string16 BrowserAccessibility::GetString16Attribute( return GetData().GetString16Attribute(attribute); } -bool BrowserAccessibility::GetString16Attribute( - ui::AXStringAttribute attribute, - base::string16* value) const { +bool BrowserAccessibility::GetString16Attribute(ui::AXStringAttribute attribute, + base::string16* value) const { return GetData().GetString16Attribute(attribute, value); } @@ -698,9 +841,7 @@ bool BrowserAccessibility::IsCellOrTableHeaderRole() const { } bool BrowserAccessibility::HasCaret() const { - if (HasState(ui::AX_STATE_EDITABLE) && - !HasState(ui::AX_STATE_RICHLY_EDITABLE) && - HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) && + if (IsSimpleTextControl() && HasIntAttribute(ui::AX_ATTR_TEXT_SEL_START) && HasIntAttribute(ui::AX_ATTR_TEXT_SEL_END)) { return true; } @@ -724,28 +865,51 @@ bool BrowserAccessibility::IsWebAreaForPresentationalIframe() const { if (!parent) return false; - BrowserAccessibility* grandparent = parent->GetParent(); - if (!grandparent) - return false; + return parent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL; +} - return grandparent->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL; +bool BrowserAccessibility::IsClickable() const { + switch (GetRole()) { + case ui::AX_ROLE_BUTTON: + case ui::AX_ROLE_CHECK_BOX: + case ui::AX_ROLE_COLOR_WELL: + case ui::AX_ROLE_DISCLOSURE_TRIANGLE: + case ui::AX_ROLE_IMAGE_MAP_LINK: + case ui::AX_ROLE_LINK: + case ui::AX_ROLE_LIST_BOX_OPTION: + case ui::AX_ROLE_MENU_BUTTON: + case ui::AX_ROLE_MENU_ITEM: + case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: + case ui::AX_ROLE_MENU_ITEM_RADIO: + case ui::AX_ROLE_MENU_LIST_OPTION: + case ui::AX_ROLE_MENU_LIST_POPUP: + case ui::AX_ROLE_POP_UP_BUTTON: + case ui::AX_ROLE_RADIO_BUTTON: + case ui::AX_ROLE_SWITCH: + case ui::AX_ROLE_TAB: + case ui::AX_ROLE_TOGGLE_BUTTON: + return true; + default: + return false; + } } bool BrowserAccessibility::IsControl() const { switch (GetRole()) { case ui::AX_ROLE_BUTTON: - case ui::AX_ROLE_BUTTON_DROP_DOWN: case ui::AX_ROLE_CHECK_BOX: case ui::AX_ROLE_COLOR_WELL: case ui::AX_ROLE_COMBO_BOX: case ui::AX_ROLE_DISCLOSURE_TRIANGLE: case ui::AX_ROLE_LIST_BOX: + case ui::AX_ROLE_MENU: case ui::AX_ROLE_MENU_BAR: case ui::AX_ROLE_MENU_BUTTON: case ui::AX_ROLE_MENU_ITEM: case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: case ui::AX_ROLE_MENU_ITEM_RADIO: - case ui::AX_ROLE_MENU: + case ui::AX_ROLE_MENU_LIST_OPTION: + case ui::AX_ROLE_MENU_LIST_POPUP: case ui::AX_ROLE_POP_UP_BUTTON: case ui::AX_ROLE_RADIO_BUTTON: case ui::AX_ROLE_SCROLL_BAR: @@ -763,14 +927,31 @@ bool BrowserAccessibility::IsControl() const { } } +bool BrowserAccessibility::IsMenuRelated() const { + switch (GetRole()) { + case ui::AX_ROLE_MENU: + case ui::AX_ROLE_MENU_BAR: + case ui::AX_ROLE_MENU_BUTTON: + case ui::AX_ROLE_MENU_ITEM: + case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: + case ui::AX_ROLE_MENU_ITEM_RADIO: + case ui::AX_ROLE_MENU_LIST_OPTION: + case ui::AX_ROLE_MENU_LIST_POPUP: + return true; + default: + return false; + } +} + bool BrowserAccessibility::IsSimpleTextControl() const { // Time fields, color wells and spinner buttons might also use text fields as // constituent parts, but they are not considered text fields as a whole. switch (GetRole()) { case ui::AX_ROLE_COMBO_BOX: case ui::AX_ROLE_SEARCH_BOX: - case ui::AX_ROLE_TEXT_FIELD: return true; + case ui::AX_ROLE_TEXT_FIELD: + return !HasState(ui::AX_STATE_RICHLY_EDITABLE); default: return false; } @@ -842,39 +1023,49 @@ void BrowserAccessibility::FixEmptyBounds(gfx::Rect* bounds) const gfx::Rect BrowserAccessibility::ElementBoundsToLocalBounds(gfx::Rect bounds) const { - // Walk up the parent chain. Every time we encounter a Web Area, offset - // based on the scroll bars and then offset based on the origin of that - // nested web area. - BrowserAccessibility* parent = GetParent(); - bool need_to_offset_web_area = - (GetRole() == ui::AX_ROLE_WEB_AREA || - GetRole() == ui::AX_ROLE_ROOT_WEB_AREA); - while (parent) { - if (need_to_offset_web_area && - parent->GetLocation().width() > 0 && - parent->GetLocation().height() > 0) { - bounds.Offset(parent->GetLocation().x(), parent->GetLocation().y()); - need_to_offset_web_area = false; - } - - // On some platforms, we don't want to take the root scroll offsets - // into account. - if (parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA && - !manager()->UseRootScrollOffsetsWhenComputingBounds()) { - break; - } - - if (parent->GetRole() == ui::AX_ROLE_WEB_AREA || - parent->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) { + BrowserAccessibilityManager* manager = this->manager(); + BrowserAccessibility* root = manager->GetRoot(); + while (manager && root) { + // Apply scroll offsets. + if (root != this && (root->GetParent() || + manager->UseRootScrollOffsetsWhenComputingBounds())) { int sx = 0; int sy = 0; - if (parent->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) && - parent->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) { + if (root->GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) && + root->GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) { bounds.Offset(-sx, -sy); } - need_to_offset_web_area = true; } - parent = parent->GetParent(); + + // If the parent accessibility tree is in a different site instance, + // ask the delegate to transform our coordinates into the root + // coordinate space and then we're done. + if (manager->delegate() && + root->GetParent() && + root->GetParent()->manager()->delegate()) { + BrowserAccessibilityManager* parent_manager = + root->GetParent()->manager(); + if (manager->delegate()->AccessibilityGetSiteInstance() != + parent_manager->delegate()->AccessibilityGetSiteInstance()) { + return manager->delegate()->AccessibilityTransformToRootCoordSpace( + bounds); + } + } + + // Otherwise, apply the transform from this frame into the coordinate + // space of its parent frame. + if (root->GetData().transform) { + gfx::RectF boundsf(bounds); + root->GetData().transform->TransformRect(&boundsf); + bounds = gfx::Rect(boundsf.x(), boundsf.y(), + boundsf.width(), boundsf.height()); + } + + if (!root->GetParent()) + break; + + manager = root->GetParent()->manager(); + root = manager->GetRoot(); } return bounds; diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h index 9b1a3feb411..49120b9c396 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.h +++ b/chromium/content/browser/accessibility/browser_accessibility.h @@ -75,6 +75,8 @@ class CONTENT_EXPORT BrowserAccessibility { virtual ~BrowserAccessibility(); + static BrowserAccessibility* GetFromUniqueID(int32_t unique_id); + // Called only once, immediately after construction. The constructor doesn't // take any arguments because in the Windows subclass we use a special // function to construct a COM object. @@ -89,6 +91,11 @@ class CONTENT_EXPORT BrowserAccessibility { // Called when the location changed. virtual void OnLocationChanged() {} + // This is called when the platform-specific attributes for a node need + // to be recomputed, which may involve firing native events, due to a + // change other than an update from OnAccessibilityEvents. + virtual void UpdatePlatformAttributes() {} + // Return true if this object is equal to or a descendant of |ancestor|. bool IsDescendantOf(const BrowserAccessibility* ancestor) const; @@ -116,6 +123,10 @@ class CONTENT_EXPORT BrowserAccessibility { // platform. bool PlatformIsChildOfLeaf() const; + // If this object is exposed to the platform, returns this object. Otherwise, + // returns the platform leaf under which this object is found. + BrowserAccessibility* GetClosestPlatformObject() const; + BrowserAccessibility* GetPreviousSibling() const; BrowserAccessibility* GetNextSibling() const; @@ -124,6 +135,11 @@ class CONTENT_EXPORT BrowserAccessibility { // Returns nullptr if there are no children. BrowserAccessibility* PlatformDeepestLastChild() const; + // Returns nullptr if there are no children. + BrowserAccessibility* InternalDeepestFirstChild() const; + // Returns nullptr if there are no children. + BrowserAccessibility* InternalDeepestLastChild() const; + // Returns the bounds of this object in coordinates relative to the // top-left corner of the overall web area. gfx::Rect GetLocalBoundsRect() const; @@ -183,8 +199,9 @@ class CONTENT_EXPORT BrowserAccessibility { // BrowserAccessibilityManager* manager() const { return manager_; } - bool instance_active() const { return node_ != NULL; } + bool instance_active() const { return node_ && manager_; } ui::AXNode* node() const { return node_; } + int32_t unique_id() const { return unique_id_; } // These access the internal accessibility tree, which doesn't necessarily // reflect the accessibility tree that should be exposed on each platform. @@ -211,17 +228,6 @@ class CONTENT_EXPORT BrowserAccessibility { // IsNative returns false. virtual bool IsNative() const; -#if defined(OS_MACOSX) && __OBJC__ - const BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa() const; - BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa(); -#elif defined(OS_WIN) - const BrowserAccessibilityWin* ToBrowserAccessibilityWin() const; - BrowserAccessibilityWin* ToBrowserAccessibilityWin(); -#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11) - const BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux() const; - BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux(); -#endif - // Accessing accessibility attributes: // // There are dozens of possible attributes for an accessibility node, @@ -245,6 +251,17 @@ class CONTENT_EXPORT BrowserAccessibility { float GetFloatAttribute(ui::AXFloatAttribute attr) const; bool GetFloatAttribute(ui::AXFloatAttribute attr, float* value) const; + bool HasInheritedStringAttribute(ui::AXStringAttribute attribute) const; + const std::string& GetInheritedStringAttribute( + ui::AXStringAttribute attribute) const; + bool GetInheritedStringAttribute(ui::AXStringAttribute attribute, + std::string* value) const; + + base::string16 GetInheritedString16Attribute( + ui::AXStringAttribute attribute) const; + bool GetInheritedString16Attribute(ui::AXStringAttribute attribute, + base::string16* value) const; + bool HasIntAttribute(ui::AXIntAttribute attribute) const; int GetIntAttribute(ui::AXIntAttribute attribute) const; bool GetIntAttribute(ui::AXIntAttribute attribute, int* value) const; @@ -255,10 +272,10 @@ class CONTENT_EXPORT BrowserAccessibility { bool GetStringAttribute(ui::AXStringAttribute attribute, std::string* value) const; - bool GetString16Attribute(ui::AXStringAttribute attribute, - base::string16* value) const; base::string16 GetString16Attribute( ui::AXStringAttribute attribute) const; + bool GetString16Attribute(ui::AXStringAttribute attribute, + base::string16* value) const; bool HasIntListAttribute(ui::AXIntListAttribute attribute) const; const std::vector& GetIntListAttribute( @@ -287,6 +304,8 @@ class CONTENT_EXPORT BrowserAccessibility { bool* is_defined, bool* is_mixed) const; + base::string16 GetFontFamily() const; + base::string16 GetLanguage() const; virtual base::string16 GetText() const; // Returns true if the bit corresponding to the given state enum is 1. @@ -301,7 +320,10 @@ class CONTENT_EXPORT BrowserAccessibility { // True if this is a web area, and its grandparent is a presentational iframe. bool IsWebAreaForPresentationalIframe() const; + virtual bool IsClickable() const; bool IsControl() const; + bool IsMenuRelated() const; + bool IsRangeControl() const; bool IsSimpleTextControl() const; // Indicates if this object is at the root of a rich edit text control. bool IsRichTextControl() const; @@ -319,6 +341,9 @@ class CONTENT_EXPORT BrowserAccessibility { // The underlying node. ui::AXNode* node_; + // A unique ID, since node IDs are frame-local. + int32_t unique_id_; + private: // |GetInnerText| recursively includes all the text from descendants such as // text found in any embedded object. In contrast, |GetText| might include a diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc index 79a1f18a369..53df2492ce4 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc @@ -5,11 +5,15 @@ #include "content/browser/accessibility/browser_accessibility_android.h" #include "base/i18n/break_iterator.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 "content/app/strings/grit/content_strings.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/common/accessibility_messages.h" +#include "content/public/common/content_client.h" +#include "third_party/skia/include/core/SkColor.h" namespace { @@ -132,6 +136,8 @@ bool BrowserAccessibilityAndroid::IsClickable() const { if (!PlatformIsLeaf()) return false; + // We are very aggressive about returning true with IsClickable on Android + // because there is no way to know for sure what might have a click handler. return IsFocusable() || !GetText().empty(); } @@ -181,7 +187,7 @@ bool BrowserAccessibilityAndroid::IsFocusable() const { } bool BrowserAccessibilityAndroid::IsFocused() const { - return manager()->GetFocus(manager()->GetRoot()) == this; + return manager()->GetFocus() == this; } bool BrowserAccessibilityAndroid::IsHeading() const { @@ -300,7 +306,10 @@ const char* BrowserAccessibilityAndroid::GetClassName() const { class_name = "android.app.Dialog"; break; case ui::AX_ROLE_ROOT_WEB_AREA: - class_name = "android.webkit.WebView"; + if (GetParent() == nullptr) + class_name = "android.webkit.WebView"; + else + class_name = "android.view.View"; break; case ui::AX_ROLE_MENU_ITEM: case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: @@ -354,10 +363,11 @@ base::string16 BrowserAccessibilityAndroid::GetText() const { // For color wells, the color is stored in separate attributes. // Perhaps we could return color names in the future? if (GetRole() == ui::AX_ROLE_COLOR_WELL) { - int color = GetIntAttribute(ui::AX_ATTR_COLOR_VALUE); - int red = (color >> 16) & 0xFF; - int green = (color >> 8) & 0xFF; - int blue = color & 0xFF; + unsigned int color = + static_cast(GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); + unsigned int red = SkColorGetR(color); + unsigned int green = SkColorGetG(color); + unsigned int blue = SkColorGetB(color); return base::UTF8ToUTF16( base::StringPrintf("#%02X%02X%02X", red, green, blue)); } @@ -407,6 +417,412 @@ base::string16 BrowserAccessibilityAndroid::GetText() const { return text; } +base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { + content::ContentClient* content_client = content::GetContentClient(); + + // As a special case, if we have a heading level return a string like + // "heading level 1", etc. + if (GetRole() == ui::AX_ROLE_HEADING) { + int level = GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL); + if (level >= 1 && level <= 6) { + std::vector values; + values.push_back(base::IntToString16(level)); + return base::ReplaceStringPlaceholders( + content_client->GetLocalizedString(IDS_AX_ROLE_HEADING_WITH_LEVEL), + values, nullptr); + } + } + + int message_id = -1; + switch (GetRole()) { + case ui::AX_ROLE_ABBR: + // No role description. + break; + case ui::AX_ROLE_ALERT_DIALOG: + message_id = IDS_AX_ROLE_ALERT_DIALOG; + break; + case ui::AX_ROLE_ALERT: + message_id = IDS_AX_ROLE_ALERT; + break; + case ui::AX_ROLE_ANNOTATION: + // No role description. + break; + case ui::AX_ROLE_APPLICATION: + message_id = IDS_AX_ROLE_APPLICATION; + break; + case ui::AX_ROLE_ARTICLE: + message_id = IDS_AX_ROLE_ARTICLE; + break; + case ui::AX_ROLE_BANNER: + message_id = IDS_AX_ROLE_BANNER; + break; + case ui::AX_ROLE_BLOCKQUOTE: + message_id = IDS_AX_ROLE_BLOCKQUOTE; + break; + case ui::AX_ROLE_BUSY_INDICATOR: + message_id = IDS_AX_ROLE_BUSY_INDICATOR; + break; + case ui::AX_ROLE_BUTTON: + message_id = IDS_AX_ROLE_BUTTON; + break; + case ui::AX_ROLE_BUTTON_DROP_DOWN: + message_id = IDS_AX_ROLE_BUTTON_DROP_DOWN; + break; + case ui::AX_ROLE_CANVAS: + // No role description. + break; + case ui::AX_ROLE_CAPTION: + // No role description. + break; + case ui::AX_ROLE_CELL: + message_id = IDS_AX_ROLE_CELL; + break; + case ui::AX_ROLE_CHECK_BOX: + message_id = IDS_AX_ROLE_CHECK_BOX; + break; + case ui::AX_ROLE_CLIENT: + // No role description. + break; + case ui::AX_ROLE_COLOR_WELL: + message_id = IDS_AX_ROLE_COLOR_WELL; + break; + case ui::AX_ROLE_COLUMN_HEADER: + message_id = IDS_AX_ROLE_COLUMN_HEADER; + break; + case ui::AX_ROLE_COLUMN: + // No role description. + break; + case ui::AX_ROLE_COMBO_BOX: + message_id = IDS_AX_ROLE_COMBO_BOX; + break; + case ui::AX_ROLE_COMPLEMENTARY: + message_id = IDS_AX_ROLE_COMPLEMENTARY; + break; + case ui::AX_ROLE_CONTENT_INFO: + message_id = IDS_AX_ROLE_CONTENT_INFO; + break; + case ui::AX_ROLE_DATE: + message_id = IDS_AX_ROLE_DATE; + break; + case ui::AX_ROLE_DATE_TIME: + message_id = IDS_AX_ROLE_DATE_TIME; + break; + case ui::AX_ROLE_DEFINITION: + message_id = IDS_AX_ROLE_DEFINITION; + break; + case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: + message_id = IDS_AX_ROLE_DEFINITION; + break; + case ui::AX_ROLE_DESCRIPTION_LIST: + // No role description. + break; + case ui::AX_ROLE_DESCRIPTION_LIST_TERM: + // No role description. + break; + case ui::AX_ROLE_DESKTOP: + // No role description. + break; + case ui::AX_ROLE_DETAILS: + // No role description. + break; + case ui::AX_ROLE_DIALOG: + message_id = IDS_AX_ROLE_DIALOG; + break; + case ui::AX_ROLE_DIRECTORY: + message_id = IDS_AX_ROLE_DIRECTORY; + break; + case ui::AX_ROLE_DISCLOSURE_TRIANGLE: + message_id = IDS_AX_ROLE_DISCLOSURE_TRIANGLE; + break; + case ui::AX_ROLE_DIV: + // No role description. + break; + case ui::AX_ROLE_DOCUMENT: + message_id = IDS_AX_ROLE_DOCUMENT; + break; + case ui::AX_ROLE_EMBEDDED_OBJECT: + message_id = IDS_AX_ROLE_EMBEDDED_OBJECT; + break; + case ui::AX_ROLE_FIGCAPTION: + // No role description. + break; + case ui::AX_ROLE_FIGURE: + message_id = IDS_AX_ROLE_GRAPHIC; + break; + case ui::AX_ROLE_FOOTER: + message_id = IDS_AX_ROLE_FOOTER; + break; + case ui::AX_ROLE_FORM: + // No role description. + break; + case ui::AX_ROLE_GRID: + message_id = IDS_AX_ROLE_TABLE; + break; + case ui::AX_ROLE_GROUP: + // No role description. + break; + case ui::AX_ROLE_HEADING: + // Note that code above this switch statement handles headings with + // a level, returning a string like "heading level 1", etc. + message_id = IDS_AX_ROLE_HEADING; + break; + case ui::AX_ROLE_IFRAME: + // No role description. + break; + case ui::AX_ROLE_IFRAME_PRESENTATIONAL: + // No role description. + break; + case ui::AX_ROLE_IGNORED: + // No role description. + break; + case ui::AX_ROLE_IMAGE_MAP_LINK: + message_id = IDS_AX_ROLE_LINK; + break; + case ui::AX_ROLE_IMAGE_MAP: + message_id = IDS_AX_ROLE_GRAPHIC; + break; + case ui::AX_ROLE_IMAGE: + message_id = IDS_AX_ROLE_GRAPHIC; + break; + case ui::AX_ROLE_INLINE_TEXT_BOX: + // No role description. + break; + case ui::AX_ROLE_INPUT_TIME: + message_id = IDS_AX_ROLE_INPUT_TIME; + break; + case ui::AX_ROLE_LABEL_TEXT: + // No role description. + break; + case ui::AX_ROLE_LEGEND: + // No role description. + break; + case ui::AX_ROLE_LINE_BREAK: + // No role description. + break; + case ui::AX_ROLE_LINK: + message_id = IDS_AX_ROLE_LINK; + break; + case ui::AX_ROLE_LIST_BOX_OPTION: + // No role description. + break; + case ui::AX_ROLE_LIST_BOX: + message_id = IDS_AX_ROLE_LIST_BOX; + break; + case ui::AX_ROLE_LIST_ITEM: + // No role description. + break; + case ui::AX_ROLE_LIST_MARKER: + // No role description. + break; + case ui::AX_ROLE_LIST: + // No role description. + break; + case ui::AX_ROLE_LOCATION_BAR: + // No role description. + break; + case ui::AX_ROLE_LOG: + message_id = IDS_AX_ROLE_LOG; + break; + case ui::AX_ROLE_MAIN: + message_id = IDS_AX_ROLE_MAIN_CONTENT; + break; + case ui::AX_ROLE_MARK: + message_id = IDS_AX_ROLE_MARK; + break; + case ui::AX_ROLE_MARQUEE: + message_id = IDS_AX_ROLE_MARQUEE; + break; + case ui::AX_ROLE_MATH: + message_id = IDS_AX_ROLE_MATH; + break; + case ui::AX_ROLE_MENU_BAR: + message_id = IDS_AX_ROLE_MENU_BAR; + break; + case ui::AX_ROLE_MENU_BUTTON: + message_id = IDS_AX_ROLE_MENU_BUTTON; + break; + case ui::AX_ROLE_MENU_ITEM: + message_id = IDS_AX_ROLE_MENU_ITEM; + break; + case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: + message_id = IDS_AX_ROLE_CHECK_BOX; + break; + case ui::AX_ROLE_MENU_ITEM_RADIO: + message_id = IDS_AX_ROLE_RADIO; + break; + case ui::AX_ROLE_MENU_LIST_OPTION: + // No role description. + break; + case ui::AX_ROLE_MENU_LIST_POPUP: + // No role description. + break; + case ui::AX_ROLE_MENU: + message_id = IDS_AX_ROLE_MENU; + break; + case ui::AX_ROLE_METER: + message_id = IDS_AX_ROLE_METER; + break; + case ui::AX_ROLE_NAVIGATION: + message_id = IDS_AX_ROLE_NAVIGATIONAL_LINK; + break; + case ui::AX_ROLE_NOTE: + message_id = IDS_AX_ROLE_NOTE; + break; + case ui::AX_ROLE_OUTLINE: + message_id = IDS_AX_ROLE_OUTLINE; + break; + case ui::AX_ROLE_PANE: + // No role description. + break; + case ui::AX_ROLE_PARAGRAPH: + // No role description. + break; + case ui::AX_ROLE_POP_UP_BUTTON: + message_id = IDS_AX_ROLE_POP_UP_BUTTON; + break; + case ui::AX_ROLE_PRE: + // No role description. + break; + case ui::AX_ROLE_PRESENTATIONAL: + // No role description. + break; + case ui::AX_ROLE_PROGRESS_INDICATOR: + message_id = IDS_AX_ROLE_PROGRESS_INDICATOR; + break; + case ui::AX_ROLE_RADIO_BUTTON: + message_id = IDS_AX_ROLE_RADIO; + break; + case ui::AX_ROLE_RADIO_GROUP: + message_id = IDS_AX_ROLE_RADIO_GROUP; + break; + case ui::AX_ROLE_REGION: + message_id = IDS_AX_ROLE_REGION; + break; + case ui::AX_ROLE_ROOT_WEB_AREA: + // No role description. + break; + case ui::AX_ROLE_ROW_HEADER: + message_id = IDS_AX_ROLE_ROW_HEADER; + break; + case ui::AX_ROLE_ROW: + // No role description. + break; + case ui::AX_ROLE_RUBY: + // No role description. + break; + case ui::AX_ROLE_RULER: + message_id = IDS_AX_ROLE_RULER; + break; + case ui::AX_ROLE_SVG_ROOT: + message_id = IDS_AX_ROLE_GRAPHIC; + break; + case ui::AX_ROLE_SCROLL_AREA: + // No role description. + break; + case ui::AX_ROLE_SCROLL_BAR: + message_id = IDS_AX_ROLE_SCROLL_BAR; + break; + case ui::AX_ROLE_SEAMLESS_WEB_AREA: + // No role description. + break; + case ui::AX_ROLE_SEARCH: + message_id = IDS_AX_ROLE_SEARCH; + break; + case ui::AX_ROLE_SEARCH_BOX: + message_id = IDS_AX_ROLE_SEARCH_BOX; + break; + case ui::AX_ROLE_SLIDER: + message_id = IDS_AX_ROLE_SLIDER; + break; + case ui::AX_ROLE_SLIDER_THUMB: + // No role description. + break; + case ui::AX_ROLE_SPIN_BUTTON_PART: + // No role description. + break; + case ui::AX_ROLE_SPIN_BUTTON: + message_id = IDS_AX_ROLE_SPIN_BUTTON; + break; + case ui::AX_ROLE_SPLITTER: + message_id = IDS_AX_ROLE_SPLITTER; + break; + case ui::AX_ROLE_STATIC_TEXT: + // No role description. + break; + case ui::AX_ROLE_STATUS: + message_id = IDS_AX_ROLE_STATUS; + break; + case ui::AX_ROLE_SWITCH: + message_id = IDS_AX_ROLE_SWITCH; + break; + case ui::AX_ROLE_TAB_GROUP: + // No role description. + break; + case ui::AX_ROLE_TAB_LIST: + message_id = IDS_AX_ROLE_TAB_LIST; + break; + case ui::AX_ROLE_TAB_PANEL: + message_id = IDS_AX_ROLE_TAB_PANEL; + break; + case ui::AX_ROLE_TAB: + message_id = IDS_AX_ROLE_TAB; + break; + case ui::AX_ROLE_TABLE_HEADER_CONTAINER: + // No role description. + break; + case ui::AX_ROLE_TABLE: + message_id = IDS_AX_ROLE_TABLE; + break; + case ui::AX_ROLE_TEXT_FIELD: + // No role description. + break; + case ui::AX_ROLE_TIME: + message_id = IDS_AX_ROLE_TIME; + break; + case ui::AX_ROLE_TIMER: + message_id = IDS_AX_ROLE_TIMER; + break; + case ui::AX_ROLE_TITLE_BAR: + // No role description. + break; + case ui::AX_ROLE_TOGGLE_BUTTON: + message_id = IDS_AX_ROLE_TOGGLE_BUTTON; + break; + case ui::AX_ROLE_TOOLBAR: + message_id = IDS_AX_ROLE_TOOLBAR; + break; + case ui::AX_ROLE_TREE_GRID: + message_id = IDS_AX_ROLE_TREE_GRID; + break; + case ui::AX_ROLE_TREE_ITEM: + message_id = IDS_AX_ROLE_TREE_ITEM; + break; + case ui::AX_ROLE_TREE: + message_id = IDS_AX_ROLE_TREE; + break; + case ui::AX_ROLE_UNKNOWN: + // No role description. + break; + case ui::AX_ROLE_TOOLTIP: + message_id = IDS_AX_ROLE_TOOLTIP; + break; + case ui::AX_ROLE_WEB_AREA: + // No role description. + break; + case ui::AX_ROLE_WEB_VIEW: + // No role description. + break; + case ui::AX_ROLE_WINDOW: + // No role description. + break; + } + + if (message_id != -1) + return content_client->GetLocalizedString(message_id); + + return base::string16(); +} + int BrowserAccessibilityAndroid::GetItemIndex() const { int index = 0; switch (GetRole()) { @@ -526,13 +942,13 @@ bool BrowserAccessibilityAndroid::Scroll(int direction) const { // Figure out the bounding box of the visible portion of this scrollable // view so we know how much to scroll by. gfx::Rect bounds; - if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) { + if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA && !GetParent()) { // If this is the root web area, use the bounds of the view to determine // how big one page is. if (!manager()->delegate()) return false; bounds = manager()->delegate()->AccessibilityGetViewBounds(); - } else if (GetRole() == ui::AX_ROLE_WEB_AREA) { + } else if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA && GetParent()) { // If this is a web area inside of an iframe, try to use the bounds of // the containing element. BrowserAccessibility* parent = GetParent(); @@ -924,9 +1340,8 @@ bool BrowserAccessibilityAndroid::HasOnlyTextAndImageChildren() const { } bool BrowserAccessibilityAndroid::IsIframe() const { - base::string16 html_tag = GetString16Attribute( - ui::AX_ATTR_HTML_TAG); - return html_tag == base::ASCIIToUTF16("iframe"); + return (GetRole() == ui::AX_ROLE_IFRAME || + GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL); } void BrowserAccessibilityAndroid::OnDataChanged() { diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.h b/chromium/content/browser/accessibility/browser_accessibility_android.h index 66055538087..56a5b63ea67 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.h +++ b/chromium/content/browser/accessibility/browser_accessibility_android.h @@ -25,7 +25,7 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility { bool IsCheckable() const; bool IsChecked() const; - bool IsClickable() const; + bool IsClickable() const override; bool IsCollection() const; bool IsCollectionItem() const; bool IsContentInvalid() const; @@ -52,6 +52,8 @@ class CONTENT_EXPORT BrowserAccessibilityAndroid : public BrowserAccessibility { const char* GetClassName() const; base::string16 GetText() const override; + base::string16 GetRoleDescription() const; + int GetItemIndex() const; int GetItemCount() const; diff --git a/chromium/content/browser/accessibility/browser_accessibility_auralinux.cc b/chromium/content/browser/accessibility/browser_accessibility_auralinux.cc index 880839e047a..231a3dd7cd4 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_auralinux.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_auralinux.cc @@ -23,6 +23,18 @@ static BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux( return atk_object->m_object; } +const BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux( + const BrowserAccessibility* obj) { + DCHECK(!obj || obj->IsNative()); + return static_cast(obj); +} + +BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux( + BrowserAccessibility* obj) { + DCHECK(!obj || obj->IsNative()); + return static_cast(obj); +} + // // AtkAction interface. // @@ -140,7 +152,7 @@ static AtkObject* browser_accessibility_accessible_at_point( return NULL; AtkObject* atk_result = - result->ToBrowserAccessibilityAuraLinux()->GetAtkObject(); + ToBrowserAccessibilityAuraLinux(result)->GetAtkObject(); g_object_ref(atk_result); return atk_result; } @@ -177,7 +189,7 @@ static gboolean browser_accessibility_grab_focus(AtkComponent* atk_component) { if (!obj) return false; - obj->manager()->SetFocus(obj, true); + obj->manager()->SetFocus(*obj); return true; } @@ -474,7 +486,7 @@ static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) { if (!obj) return NULL; if (obj->GetParent()) - return obj->GetParent()->ToBrowserAccessibilityAuraLinux()->GetAtkObject(); + return ToBrowserAccessibilityAuraLinux(obj->GetParent())->GetAtkObject(); BrowserAccessibilityManagerAuraLinux* manager = static_cast(obj->manager()); @@ -500,9 +512,8 @@ static AtkObject* browser_accessibility_ref_child(AtkObject* atk_object, if (index < 0 || index >= static_cast(obj->PlatformChildCount())) return NULL; - AtkObject* result = obj->InternalGetChild(index) - ->ToBrowserAccessibilityAuraLinux() - ->GetAtkObject(); + AtkObject* result = ToBrowserAccessibilityAuraLinux( + obj->InternalGetChild(index))->GetAtkObject(); g_object_ref(result); return result; } @@ -539,7 +550,7 @@ static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) { if (state & (1 << ui::AX_STATE_FOCUSABLE)) atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE); - if (obj->manager()->GetFocus(NULL) == obj) + if (obj->manager()->GetFocus() == obj) atk_state_set_add_state(state_set, ATK_STATE_FOCUSED); if (state & (1 << ui::AX_STATE_ENABLED)) atk_state_set_add_state(state_set, ATK_STATE_ENABLED); @@ -735,16 +746,6 @@ BrowserAccessibility* BrowserAccessibility::Create() { return new BrowserAccessibilityAuraLinux(); } -const BrowserAccessibilityAuraLinux* -BrowserAccessibility::ToBrowserAccessibilityAuraLinux() const { - return static_cast(this); -} - -BrowserAccessibilityAuraLinux* -BrowserAccessibility::ToBrowserAccessibilityAuraLinux() { - return static_cast(this); -} - BrowserAccessibilityAuraLinux::BrowserAccessibilityAuraLinux() : atk_object_(NULL) { } @@ -782,7 +783,7 @@ void BrowserAccessibilityAuraLinux::OnDataChanged() { if (this->GetParent()) { atk_object_set_parent( atk_object_, - this->GetParent()->ToBrowserAccessibilityAuraLinux()->GetAtkObject()); + ToBrowserAccessibilityAuraLinux(this->GetParent())->GetAtkObject()); } } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_auralinux.h b/chromium/content/browser/accessibility/browser_accessibility_auralinux.h index 43a9c3f16b0..773e79fbd31 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_auralinux.h +++ b/chromium/content/browser/accessibility/browser_accessibility_auralinux.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "content/browser/accessibility/browser_accessibility.h" +#include "content/common/content_export.h" namespace content { @@ -88,6 +89,12 @@ class BrowserAccessibilityAuraLinux : public BrowserAccessibility { DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityAuraLinux); }; +CONTENT_EXPORT const BrowserAccessibilityAuraLinux* +ToBrowserAccessibilityAuraLinux(const BrowserAccessibility* obj); + +CONTENT_EXPORT BrowserAccessibilityAuraLinux* ToBrowserAccessibilityAuraLinux( + BrowserAccessibility* obj); + } // namespace content #endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_AURALINUX_H_ diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h index 6c5a3e6f0c1..992373453e6 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.h +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.h @@ -22,8 +22,8 @@ } // This creates a cocoa browser accessibility object around -// the cross platform BrowserAccessibility object, which can't be null. -- (id)initWithObject:(content::BrowserAccessibility*)accessibility; +// the cross platform BrowserAccessibility object, which can't be nullptr. +- (instancetype)initWithObject:(content::BrowserAccessibility*)accessibility; // Clear this object's pointer to the wrapped BrowserAccessibility object // because the wrapped object has been deleted, but this object may @@ -39,7 +39,7 @@ // Convenience method to determine if this object should expose its // accessible name in AXValue (as opposed to AXTitle/AXDescription). -- (bool)shouldExposeNameInAXValue; +- (BOOL)shouldExposeNameInAXValue; // Convenience method to get the BrowserAccessibilityDelegate from // the manager. @@ -48,6 +48,9 @@ // Get the BrowserAccessibility that this object wraps. - (content::BrowserAccessibility*)browserAccessibility; +// Determines if this object is alive, i.e. it hasn't been detached. +- (BOOL)instanceActive; + // Convert the local objet's origin to a global point. - (NSPoint)pointInScreen:(NSPoint)origin size:(NSSize)size; @@ -61,16 +64,16 @@ // Returns the requested text range from this object's value attribute. - (NSString*)valueForRange:(NSRange)range; -// Internally-used method. +// Internally-used property. @property(nonatomic, readonly) NSPoint origin; -// Children is an array of BrowserAccessibility objects, representing -// the accessibility children of this object. @property(nonatomic, readonly) NSString* accessKey; @property(nonatomic, readonly) NSNumber* ariaAtomic; @property(nonatomic, readonly) NSNumber* ariaBusy; @property(nonatomic, readonly) NSString* ariaLive; +@property(nonatomic, readonly) NSNumber* ariaPosInSet; @property(nonatomic, readonly) NSString* ariaRelevant; +@property(nonatomic, readonly) NSNumber* ariaSetSize; @property(nonatomic, readonly) NSArray* children; @property(nonatomic, readonly) NSArray* columns; @property(nonatomic, readonly) NSArray* columnHeaders; @@ -80,15 +83,24 @@ @property(nonatomic, readonly) id disclosedByRow; @property(nonatomic, readonly) NSNumber* disclosureLevel; @property(nonatomic, readonly) id disclosedRows; +@property(nonatomic, readonly) NSString* dropEffects; @property(nonatomic, readonly) NSNumber* enabled; +// Returns a text marker that points to the last character in the document that +// can be selected with Voiceover. +@property(nonatomic, readonly) id endTextMarker; +@property(nonatomic, readonly) NSNumber* expanded; @property(nonatomic, readonly) NSNumber* focused; +@property(nonatomic, readonly) NSNumber* grabbed; +@property(nonatomic, readonly) id header; @property(nonatomic, readonly) NSString* help; // isIgnored returns whether or not the accessibility object // should be ignored by the accessibility hierarchy. @property(nonatomic, readonly, getter=isIgnored) BOOL ignored; // Index of a row, column, or tree item. @property(nonatomic, readonly) NSNumber* index; +@property(nonatomic, readonly) NSNumber* insertionPointLineNumber; @property(nonatomic, readonly) NSString* invalid; +@property(nonatomic, readonly) NSNumber* isMultiSelectable; @property(nonatomic, readonly) NSString* placeholderValue; @property(nonatomic, readonly) NSNumber* loaded; @property(nonatomic, readonly) NSNumber* loadingProgress; @@ -106,9 +118,17 @@ @property(nonatomic, readonly) NSArray* rowHeaders; @property(nonatomic, readonly) NSValue* rowIndexRange; @property(nonatomic, readonly) NSArray* rows; +// The object is selected as a whole. +@property(nonatomic, readonly) NSNumber* selected; @property(nonatomic, readonly) NSArray* selectedChildren; -// The size of this object. +@property(nonatomic, readonly) NSString* selectedText; +@property(nonatomic, readonly) NSValue* selectedTextRange; +@property(nonatomic, readonly) id selectedTextMarkerRange; @property(nonatomic, readonly) NSValue* size; +@property(nonatomic, readonly) NSString* sortDirection; +// Returns a text marker that points to the first character in the document that +// can be selected with Voiceover. +@property(nonatomic, readonly) id startTextMarker; // A string indicating the subrole of this object as far as accessibility // is concerned. @property(nonatomic, readonly) NSString* subrole; diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm index 78c8c0150fb..1ae72bd2e30 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -10,22 +10,21 @@ #include +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" #include "base/strings/string16.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "content/app/strings/grit/content_strings.h" +#include "content/browser/accessibility/browser_accessibility_mac.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_manager_mac.h" #include "content/browser/accessibility/one_shot_accessibility_tree_search.h" #include "content/public/common/content_client.h" +#include "third_party/skia/include/core/SkColor.h" #import "ui/accessibility/platform/ax_platform_node_mac.h" -// See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5, -// 10.6, and 10.7. It allows accessibility clients to observe events posted on -// this object. -extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element); - -using ui::AXNodeData; +using content::AXTreeIDRegistry; using content::AccessibilityMatchPredicate; using content::BrowserAccessibility; using content::BrowserAccessibilityDelegate; @@ -33,13 +32,186 @@ using content::BrowserAccessibilityManager; using content::BrowserAccessibilityManagerMac; using content::ContentClient; using content::OneShotAccessibilityTreeSearch; -typedef ui::AXStringAttribute StringAttribute; +using ui::AXNodeData; +using StringAttribute = ui::AXStringAttribute; +using AXTextMarkerRef = CFTypeRef; +using AXTextMarkerRangeRef = CFTypeRef; namespace { +// Private WebKit accessibility attributes. +NSString* const NSAccessibilityARIAAtomicAttribute = @"AXARIAAtomic"; +NSString* const NSAccessibilityARIABusyAttribute = @"AXARIABusy"; +NSString* const NSAccessibilityARIALiveAttribute = @"AXARIALive"; +NSString* const NSAccessibilityARIAPosInSetAttribute = @"AXARIAPosInSet"; +NSString* const NSAccessibilityARIARelevantAttribute = @"AXARIARelevant"; +NSString* const NSAccessibilityARIASetSizeAttribute = @"AXARIASetSize"; +NSString* const NSAccessibilityAccessKeyAttribute = @"AXAccessKey"; +NSString* const NSAccessibilityDropEffectsAttribute = @"AXDropEffects"; +NSString* const NSAccessibilityGrabbedAttribute = @"AXGrabbed"; +NSString* const NSAccessibilityInvalidAttribute = @"AXInvalid"; +NSString* const NSAccessibilityIsMultiSelectableAttribute = + @"AXIsMultiSelectable"; +NSString* const NSAccessibilityLoadingProgressAttribute = @"AXLoadingProgress"; +NSString* const NSAccessibilityRequiredAttribute = @"AXRequired"; +NSString* const + NSAccessibilityUIElementCountForSearchPredicateParameterizedAttribute = + @"AXUIElementCountForSearchPredicate"; +NSString* const + NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute = + @"AXUIElementsForSearchPredicate"; +NSString* const NSAccessibilityVisitedAttribute = @"AXVisited"; + +// Private attributes for text markers. +NSString* const NSAccessibilityStartTextMarkerAttribute = @"AXStartTextMarker"; +NSString* const NSAccessibilityEndTextMarkerAttribute = @"AXEndTextMarker"; +NSString* const NSAccessibilitySelectedTextMarkerRangeAttribute = + @"AXSelectedTextMarkerRange"; +NSString* const NSAccessibilityTextMarkerIsValidParameterizedAttribute = + @"AXTextMarkerIsValid"; +NSString* const NSAccessibilityIndexForTextMarkerParameterizedAttribute = + @"AXIndexForTextMarker"; +NSString* const NSAccessibilityTextMarkerForIndexParameterizedAttribute = + @"AXTextMarkerForIndex"; +NSString* const NSAccessibilityEndTextMarkerForBoundsParameterizedAttribute = + @"AXEndTextMarkerForBounds"; +NSString* const NSAccessibilityStartTextMarkerForBoundsParameterizedAttribute = + @"AXStartTextMarkerForBounds"; +NSString* const + NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute = + @"AXLineTextMarkerRangeForTextMarker"; +NSString* const NSAccessibilitySelectTextWithCriteriaParameterizedAttribute = + @"AXSelectTextWithCriteria"; + +// Actions. +NSString* const NSAccessibilityScrollToVisibleAction = @"AXScrollToVisible"; + +// A mapping from an accessibility attribute to its method name. +NSDictionary* attributeToMethodNameMap = nil; + +struct AXTextMarkerData { + AXTreeIDRegistry::AXTreeID tree_id; + int32_t node_id; + int offset; +}; + // VoiceOver uses -1 to mean "no limit" for AXResultsLimit. const int kAXResultsLimitNoLimit = -1; +extern "C" { + +// See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5, +// 10.6, and 10.7. It allows accessibility clients to observe events posted on +// this object. +void NSAccessibilityUnregisterUniqueIdForUIElement(id element); + +// The following are private accessibility APIs required for cursor navigation +// and text selection. VoiceOver started relying on them in Mac OS X 10.11. +#if !defined(MAC_OS_X_VERSION_10_11) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 + +AXTextMarkerRef AXTextMarkerCreate(CFAllocatorRef allocator, + const UInt8* bytes, + CFIndex length); + +const UInt8* AXTextMarkerGetBytePtr(AXTextMarkerRef text_marker); + +AXTextMarkerRangeRef AXTextMarkerRangeCreate(CFAllocatorRef allocator, + AXTextMarkerRef start_marker, + AXTextMarkerRef end_marker); + +AXTextMarkerRef AXTextMarkerRangeCopyStartMarker( + AXTextMarkerRangeRef text_marker_range); + +AXTextMarkerRef AXTextMarkerRangeCopyEndMarker( + AXTextMarkerRangeRef text_marker_range); + +#endif // MAC_OS_X_VERSION_10_11 + +} // extern "C" + +id CreateTextMarker(const BrowserAccessibility& object, int offset) { + AXTextMarkerData marker_data; + marker_data.tree_id = object.manager() ? object.manager()->ax_tree_id() : -1; + marker_data.node_id = object.GetId(); + marker_data.offset = offset; + return (id)base::mac::CFTypeRefToNSObjectAutorelease(AXTextMarkerCreate( + kCFAllocatorDefault, reinterpret_cast(&marker_data), + sizeof(marker_data))); +} + +id CreateTextMarkerRange(const BrowserAccessibility& start_object, + int start_offset, + const BrowserAccessibility& end_object, + int end_offset) { + id start_marker = CreateTextMarker(start_object, start_offset); + id end_marker = CreateTextMarker(end_object, end_offset); + return (id)base::mac::CFTypeRefToNSObjectAutorelease( + AXTextMarkerRangeCreate(kCFAllocatorDefault, start_marker, end_marker)); +} + +bool GetTextMarkerData(AXTextMarkerRef text_marker, + BrowserAccessibility** object, + int* offset) { + DCHECK(text_marker); + DCHECK(object && offset); + auto marker_data = reinterpret_cast( + AXTextMarkerGetBytePtr(text_marker)); + if (!marker_data) + return false; + + const BrowserAccessibilityManager* manager = + BrowserAccessibilityManager::FromID(marker_data->tree_id); + if (!manager) + return false; + + *object = manager->GetFromID(marker_data->node_id); + if (!*object) + return false; + + *offset = marker_data->offset; + if (*offset < 0) + return false; + + return true; +} + +bool GetTextMarkerRange(AXTextMarkerRangeRef marker_range, + BrowserAccessibility** start_object, + int* start_offset, + BrowserAccessibility** end_object, + int* end_offset) { + DCHECK(marker_range); + DCHECK(start_object && start_offset); + DCHECK(end_object && end_offset); + + base::ScopedCFTypeRef start_marker( + AXTextMarkerRangeCopyStartMarker(marker_range)); + base::ScopedCFTypeRef end_marker( + AXTextMarkerRangeCopyEndMarker(marker_range)); + if (!start_marker.get() || !end_marker.get()) + return false; + + return GetTextMarkerData(start_marker.get(), start_object, start_offset) && + GetTextMarkerData(end_marker.get(), end_object, end_offset); +} + +NSString* GetTextForTextMarkerRange(AXTextMarkerRangeRef marker_range) { + BrowserAccessibility* start_object; + BrowserAccessibility* end_object; + int start_offset, end_offset; + if (!GetTextMarkerRange(marker_range, &start_object, &start_offset, + &end_object, &end_offset)) { + return nil; + } + DCHECK(start_object && end_object); + DCHECK_GE(start_offset, 0); + DCHECK_GE(end_offset, 0); + + return base::SysUTF16ToNSString(BrowserAccessibilityManager::GetTextForRange( + *start_object, start_offset, *end_object, end_offset)); +} + // Returns an autoreleased copy of the AXNodeData's attribute. NSString* NSStringForStringAttribute( BrowserAccessibility* browserAccessibility, @@ -54,9 +226,6 @@ bool GetState(BrowserAccessibility* accessibility, ui::AXState state) { return ((accessibility->GetState() >> state) & 1); } -// A mapping from an accessibility attribute to its method name. -NSDictionary* attributeToMethodNameMap = nil; - // Given a search key provided to AXUIElementCountForSearchPredicate or // AXUIElementsForSearchPredicate, return a predicate that can be added // to OneShotAccessibilityTreeSearch. @@ -65,39 +234,19 @@ AccessibilityMatchPredicate PredicateForSearchKey(NSString* searchKey) { return [](BrowserAccessibility* start, BrowserAccessibility* current) { return true; }; - } else if ([searchKey isEqualToString:@"AXBlockquoteSameLevelSearchKey"] || - [searchKey isEqualToString:@"AXBlockquoteSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - // TODO(dmazzoni): implement the "same level" part. - return current->GetRole() == ui::AX_ROLE_BLOCKQUOTE; - }; + } else if ([searchKey isEqualToString:@"AXBlockquoteSameLevelSearchKey"]) { + // TODO(dmazzoni): implement the "same level" part. + return content::AccessibilityBlockquotePredicate; + } else if ([searchKey isEqualToString:@"AXBlockquoteSearchKey"]) { + return content::AccessibilityBlockquotePredicate; } else if ([searchKey isEqualToString:@"AXBoldFontSearchKey"]) { - // TODO(dmazzoni): implement this. - return nullptr; + return content::AccessibilityTextStyleBoldPredicate; } else if ([searchKey isEqualToString:@"AXButtonSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_BUTTON || - current->GetRole() == ui::AX_ROLE_MENU_BUTTON || - current->GetRole() == ui::AX_ROLE_POP_UP_BUTTON || - current->GetRole() == ui::AX_ROLE_SWITCH || - current->GetRole() == ui::AX_ROLE_TOGGLE_BUTTON); - }; + return content::AccessibilityButtonPredicate; } else if ([searchKey isEqualToString:@"AXCheckBoxSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_CHECK_BOX || - current->GetRole() == ui::AX_ROLE_MENU_ITEM_CHECK_BOX); - }; + return content::AccessibilityCheckboxPredicate; } else if ([searchKey isEqualToString:@"AXControlSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - if (current->IsControl()) - return true; - if (current->HasState(ui::AX_STATE_FOCUSABLE) && - current->GetRole() != ui::AX_ROLE_IMAGE_MAP_LINK && - current->GetRole() != ui::AX_ROLE_LINK) { - return true; - } - return false; - }; + return content::AccessibilityControlPredicate; } else if ([searchKey isEqualToString:@"AXDifferentTypeSearchKey"]) { return [](BrowserAccessibility* start, BrowserAccessibility* current) { return current->GetRole() != start->GetRole(); @@ -109,102 +258,48 @@ AccessibilityMatchPredicate PredicateForSearchKey(NSString* searchKey) { // TODO(dmazzoni): implement this. return nullptr; } else if ([searchKey isEqualToString:@"AXFrameSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - if (current->IsWebAreaForPresentationalIframe()) - return false; - if (!current->GetParent()) - return false; - return (current->GetRole() == ui::AX_ROLE_WEB_AREA || - current->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA); - }; + return content::AccessibilityFramePredicate; } else if ([searchKey isEqualToString:@"AXGraphicSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_IMAGE; - }; + return content::AccessibilityGraphicPredicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel1SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 1); - }; + return content::AccessibilityH1Predicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel2SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 2); - }; + return content::AccessibilityH2Predicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel3SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 3); - }; + return content::AccessibilityH3Predicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel4SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 4); - }; + return content::AccessibilityH4Predicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel5SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 5); - }; + return content::AccessibilityH5Predicate; } else if ([searchKey isEqualToString:@"AXHeadingLevel6SearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 6); - }; + return content::AccessibilityH6Predicate; } else if ([searchKey isEqualToString:@"AXHeadingSameLevelSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_HEADING && - start->GetRole() == ui::AX_ROLE_HEADING && - (current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == - start->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL))); - }; + return content::AccessibilityHeadingSameLevelPredicate; } else if ([searchKey isEqualToString:@"AXHeadingSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_HEADING; - }; + return content::AccessibilityHeadingPredicate; } else if ([searchKey isEqualToString:@"AXHighlightedSearchKey"]) { // TODO(dmazzoni): implement this. return nullptr; } else if ([searchKey isEqualToString:@"AXItalicFontSearchKey"]) { - // TODO(dmazzoni): implement this. - return nullptr; + return content::AccessibilityTextStyleItalicPredicate; } else if ([searchKey isEqualToString:@"AXLandmarkSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_APPLICATION || - current->GetRole() == ui::AX_ROLE_BANNER || - current->GetRole() == ui::AX_ROLE_COMPLEMENTARY || - current->GetRole() == ui::AX_ROLE_CONTENT_INFO || - current->GetRole() == ui::AX_ROLE_FORM || - current->GetRole() == ui::AX_ROLE_MAIN || - current->GetRole() == ui::AX_ROLE_NAVIGATION || - current->GetRole() == ui::AX_ROLE_SEARCH); - }; + return content::AccessibilityLandmarkPredicate; } else if ([searchKey isEqualToString:@"AXLinkSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_LINK; - }; + return content::AccessibilityLinkPredicate; } else if ([searchKey isEqualToString:@"AXListSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_LIST; - }; + return content::AccessibilityListPredicate; } else if ([searchKey isEqualToString:@"AXLiveRegionSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS); - }; + return content::AccessibilityLiveRegionPredicate; } else if ([searchKey isEqualToString:@"AXMisspelledWordSearchKey"]) { // TODO(dmazzoni): implement this. return nullptr; } else if ([searchKey isEqualToString:@"AXOutlineSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_TREE; - }; + return content::AccessibilityTreePredicate; } else if ([searchKey isEqualToString:@"AXPlainTextSearchKey"]) { // TODO(dmazzoni): implement this. return nullptr; } else if ([searchKey isEqualToString:@"AXRadioGroupSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_RADIO_GROUP; - }; + return content::AccessibilityRadioGroupPredicate; } else if ([searchKey isEqualToString:@"AXSameTypeSearchKey"]) { return [](BrowserAccessibility* start, BrowserAccessibility* current) { return current->GetRole() == start->GetRole(); @@ -217,33 +312,18 @@ AccessibilityMatchPredicate PredicateForSearchKey(NSString* searchKey) { // TODO(dmazzoni): implement this. return nullptr; } else if ([searchKey isEqualToString:@"AXTableSameLevelSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - // TODO(dmazzoni): implement the "same level" part. - return current->GetRole() == ui::AX_ROLE_GRID || - current->GetRole() == ui::AX_ROLE_TABLE; - }; + // TODO(dmazzoni): implement the "same level" part. + return content::AccessibilityTablePredicate; } else if ([searchKey isEqualToString:@"AXTableSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->GetRole() == ui::AX_ROLE_GRID || - current->GetRole() == ui::AX_ROLE_TABLE; - }; + return content::AccessibilityTablePredicate; } else if ([searchKey isEqualToString:@"AXTextFieldSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return current->IsSimpleTextControl() || current->IsRichTextControl(); - }; + return content::AccessibilityTextfieldPredicate; } else if ([searchKey isEqualToString:@"AXUnderlineSearchKey"]) { - // TODO(dmazzoni): implement this. - return nullptr; + return content::AccessibilityTextStyleUnderlinePredicate; } else if ([searchKey isEqualToString:@"AXUnvisitedLinkSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_LINK && - !current->HasState(ui::AX_STATE_VISITED)); - }; + return content::AccessibilityUnvisitedLinkPredicate; } else if ([searchKey isEqualToString:@"AXVisitedLinkSearchKey"]) { - return [](BrowserAccessibility* start, BrowserAccessibility* current) { - return (current->GetRole() == ui::AX_ROLE_LINK && - current->HasState(ui::AX_STATE_VISITED)); - }; + return content::AccessibilityVisitedLinkPredicate; } return nullptr; @@ -330,7 +410,7 @@ bool InitializeAccessibilityTreeSearch( return true; } -} // namespace +} // namespace @implementation BrowserAccessibilityCocoa @@ -339,67 +419,78 @@ bool InitializeAccessibilityTreeSearch( NSString* attribute; NSString* methodName; } attributeToMethodNameContainer[] = { - { NSAccessibilityChildrenAttribute, @"children" }, - { NSAccessibilityColumnsAttribute, @"columns" }, - { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" }, - { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" }, - { NSAccessibilityContentsAttribute, @"contents" }, - { NSAccessibilityDescriptionAttribute, @"description" }, - { NSAccessibilityDisclosingAttribute, @"disclosing" }, - { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" }, - { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" }, - { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" }, - { NSAccessibilityEnabledAttribute, @"enabled" }, - { NSAccessibilityExpandedAttribute, @"expanded" }, - { NSAccessibilityFocusedAttribute, @"focused" }, - { NSAccessibilityHeaderAttribute, @"header" }, - { NSAccessibilityHelpAttribute, @"help" }, - { NSAccessibilityIndexAttribute, @"index" }, - { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" }, - { NSAccessibilityMaxValueAttribute, @"maxValue" }, - { NSAccessibilityMinValueAttribute, @"minValue" }, - { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" }, - { NSAccessibilityOrientationAttribute, @"orientation" }, - { NSAccessibilityParentAttribute, @"parent" }, - { NSAccessibilityPlaceholderValueAttribute, @"placeholderValue" }, - { NSAccessibilityPositionAttribute, @"position" }, - { NSAccessibilityRoleAttribute, @"role" }, - { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" }, - { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" }, - { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" }, - { NSAccessibilityRowsAttribute, @"rows" }, - // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute - { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" }, - { NSAccessibilitySizeAttribute, @"size" }, - { NSAccessibilitySubroleAttribute, @"subrole" }, - { NSAccessibilityTabsAttribute, @"tabs" }, - { NSAccessibilityTitleAttribute, @"title" }, - { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" }, - { NSAccessibilityTopLevelUIElementAttribute, @"window" }, - { NSAccessibilityURLAttribute, @"url" }, - { NSAccessibilityValueAttribute, @"value" }, - { NSAccessibilityValueDescriptionAttribute, @"valueDescription" }, - { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" }, - { NSAccessibilityVisibleCellsAttribute, @"visibleCells" }, - { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" }, - { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" }, - { NSAccessibilityVisibleRowsAttribute, @"visibleRows" }, - { NSAccessibilityWindowAttribute, @"window" }, - { @"AXAccessKey", @"accessKey" }, - { @"AXARIAAtomic", @"ariaAtomic" }, - { @"AXARIABusy", @"ariaBusy" }, - { @"AXARIALive", @"ariaLive" }, - { @"AXARIASetSize", @"ariaSetSize" }, - { @"AXARIAPosInSet", @"ariaPosInSet" }, - { @"AXARIARelevant", @"ariaRelevant" }, - { @"AXDropEffects", @"dropeffect" }, - { @"AXGrabbed", @"grabbed" }, - { @"AXInvalid", @"invalid" }, - { @"AXLoaded", @"loaded" }, - { @"AXLoadingProgress", @"loadingProgress" }, - { @"AXRequired", @"required" }, - { @"AXSortDirection", @"sortDirection" }, - { @"AXVisited", @"visited" }, + {NSAccessibilityARIAAtomicAttribute, @"ariaAtomic"}, + {NSAccessibilityARIABusyAttribute, @"ariaBusy"}, + {NSAccessibilityARIALiveAttribute, @"ariaLive"}, + {NSAccessibilityARIAPosInSetAttribute, @"ariaPosInSet"}, + {NSAccessibilityARIARelevantAttribute, @"ariaRelevant"}, + {NSAccessibilityARIASetSizeAttribute, @"ariaSetSize"}, + {NSAccessibilityAccessKeyAttribute, @"accessKey"}, + {NSAccessibilityChildrenAttribute, @"children"}, + {NSAccessibilityColumnsAttribute, @"columns"}, + {NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders"}, + {NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange"}, + {NSAccessibilityContentsAttribute, @"contents"}, + {NSAccessibilityDescriptionAttribute, @"description"}, + {NSAccessibilityDisclosingAttribute, @"disclosing"}, + {NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow"}, + {NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel"}, + {NSAccessibilityDisclosedRowsAttribute, @"disclosedRows"}, + {NSAccessibilityDropEffectsAttribute, @"dropEffects"}, + {NSAccessibilityEnabledAttribute, @"enabled"}, + {NSAccessibilityEndTextMarkerAttribute, @"endTextMarker"}, + {NSAccessibilityExpandedAttribute, @"expanded"}, + {NSAccessibilityFocusedAttribute, @"focused"}, + {NSAccessibilityGrabbedAttribute, @"grabbed"}, + {NSAccessibilityHeaderAttribute, @"header"}, + {NSAccessibilityHelpAttribute, @"help"}, + {NSAccessibilityIndexAttribute, @"index"}, + {NSAccessibilityInsertionPointLineNumberAttribute, + @"insertionPointLineNumber"}, + {NSAccessibilityInvalidAttribute, @"invalid"}, + {NSAccessibilityIsMultiSelectableAttribute, @"isMultiSelectable"}, + {NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements"}, + {NSAccessibilityLoadingProgressAttribute, @"loadingProgress"}, + {NSAccessibilityMaxValueAttribute, @"maxValue"}, + {NSAccessibilityMinValueAttribute, @"minValue"}, + {NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters"}, + {NSAccessibilityOrientationAttribute, @"orientation"}, + {NSAccessibilityParentAttribute, @"parent"}, + {NSAccessibilityPlaceholderValueAttribute, @"placeholderValue"}, + {NSAccessibilityPositionAttribute, @"position"}, + {NSAccessibilityRequiredAttribute, @"required"}, + {NSAccessibilityRoleAttribute, @"role"}, + {NSAccessibilityRoleDescriptionAttribute, @"roleDescription"}, + {NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders"}, + {NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange"}, + {NSAccessibilityRowsAttribute, @"rows"}, + // TODO(aboxhall): expose + // NSAccessibilityServesAsTitleForUIElementsAttribute + {NSAccessibilityStartTextMarkerAttribute, @"startTextMarker"}, + {NSAccessibilitySelectedAttribute, @"selected"}, + {NSAccessibilitySelectedChildrenAttribute, @"selectedChildren"}, + {NSAccessibilitySelectedTextAttribute, @"selectedText"}, + {NSAccessibilitySelectedTextRangeAttribute, @"selectedTextRange"}, + {NSAccessibilitySelectedTextMarkerRangeAttribute, + @"selectedTextMarkerRange"}, + {NSAccessibilitySizeAttribute, @"size"}, + {NSAccessibilitySortDirectionAttribute, @"sortDirection"}, + {NSAccessibilitySubroleAttribute, @"subrole"}, + {NSAccessibilityTabsAttribute, @"tabs"}, + {NSAccessibilityTitleAttribute, @"title"}, + {NSAccessibilityTitleUIElementAttribute, @"titleUIElement"}, + {NSAccessibilityTopLevelUIElementAttribute, @"window"}, + {NSAccessibilityURLAttribute, @"url"}, + {NSAccessibilityValueAttribute, @"value"}, + {NSAccessibilityValueDescriptionAttribute, @"valueDescription"}, + {NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange"}, + {NSAccessibilityVisibleCellsAttribute, @"visibleCells"}, + {NSAccessibilityVisibleChildrenAttribute, @"visibleChildren"}, + {NSAccessibilityVisibleColumnsAttribute, @"visibleColumns"}, + {NSAccessibilityVisibleRowsAttribute, @"visibleRows"}, + {NSAccessibilityVisitedAttribute, @"visited"}, + {NSAccessibilityWindowAttribute, @"window"}, + {@"AXLoaded", @"loaded"}, }; NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; @@ -413,51 +504,64 @@ bool InitializeAccessibilityTreeSearch( dict = nil; } -- (id)initWithObject:(BrowserAccessibility*)accessibility { +- (instancetype)initWithObject:(BrowserAccessibility*)accessibility { if ((self = [super init])) browserAccessibility_ = accessibility; return self; } - (void)detach { - if (browserAccessibility_) { + if (browserAccessibility_) NSAccessibilityUnregisterUniqueIdForUIElement(self); - browserAccessibility_ = NULL; - } + browserAccessibility_ = nullptr; } - (NSString*)accessKey { + if (![self instanceActive]) + return nil; return NSStringForStringAttribute( browserAccessibility_, ui::AX_ATTR_ACCESS_KEY); } - (NSNumber*)ariaAtomic { + if (![self instanceActive]) + return nil; bool boolValue = browserAccessibility_->GetBoolAttribute( ui::AX_ATTR_LIVE_ATOMIC); return [NSNumber numberWithBool:boolValue]; } - (NSNumber*)ariaBusy { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_BUSY)]; } - (NSString*)ariaLive { + if (![self instanceActive]) + return nil; return NSStringForStringAttribute( browserAccessibility_, ui::AX_ATTR_LIVE_STATUS); } -- (NSString*)ariaRelevant { - return NSStringForStringAttribute( - browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT); -} - - (NSNumber*)ariaPosInSet { + if (![self instanceActive]) + return nil; return [NSNumber numberWithInt: browserAccessibility_->GetIntAttribute(ui::AX_ATTR_POS_IN_SET)]; } +- (NSString*)ariaRelevant { + if (![self instanceActive]) + return nil; + return NSStringForStringAttribute(browserAccessibility_, + ui::AX_ATTR_LIVE_RELEVANT); +} + - (NSNumber*)ariaSetSize { + if (![self instanceActive]) + return nil; return [NSNumber numberWithInt: browserAccessibility_->GetIntAttribute(ui::AX_ATTR_SET_SIZE)]; } @@ -465,13 +569,15 @@ bool InitializeAccessibilityTreeSearch( // Returns an array of BrowserAccessibilityCocoa objects, representing the // accessibility children of this object. - (NSArray*)children { + if (![self instanceActive]) + return nil; if (!children_) { uint32_t childCount = browserAccessibility_->PlatformChildCount(); children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]); for (uint32_t index = 0; index < childCount; ++index) { BrowserAccessibilityCocoa* child = - browserAccessibility_->PlatformGetChild(index)-> - ToBrowserAccessibilityCocoa(); + ToBrowserAccessibilityCocoa( + browserAccessibility_->PlatformGetChild(index)); if ([child isIgnored]) [children_ addObjectsFromArray:[child children]]; else @@ -491,7 +597,7 @@ bool InitializeAccessibilityTreeSearch( // a DCHECK in the future. if (child) { BrowserAccessibilityCocoa* child_cocoa = - child->ToBrowserAccessibilityCocoa(); + ToBrowserAccessibilityCocoa(child); [children_ addObject:child_cocoa]; } } @@ -500,15 +606,19 @@ bool InitializeAccessibilityTreeSearch( } - (void)childrenChanged { + if (![self instanceActive]) + return; if (![self isIgnored]) { children_.reset(); } else { - [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa() - childrenChanged]; + [ToBrowserAccessibilityCocoa(browserAccessibility_->GetParent()) + childrenChanged]; } } - (NSArray*)columnHeaders { + if (![self instanceActive]) + return nil; if ([self internalRole] != ui::AX_ROLE_TABLE && [self internalRole] != ui::AX_ROLE_GRID) { return nil; @@ -522,12 +632,14 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromID(id); if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) - [ret addObject:cell->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(cell)]; } return ret; } - (NSValue*)columnIndexRange { + if (![self instanceActive]) + return nil; if (!browserAccessibility_->IsCellOrTableHeaderRole()) return nil; @@ -543,6 +655,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSArray*)columns { + if (![self instanceActive]) + return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; for (BrowserAccessibilityCocoa* child in [self children]) { if ([[child role] isEqualToString:NSAccessibilityColumnRole]) @@ -552,6 +666,9 @@ bool InitializeAccessibilityTreeSearch( } - (NSString*)description { + if (![self instanceActive]) + return nil; + // Mac OS X wants static text exposed in AXValue. if ([self shouldExposeNameInAXValue]) return @""; @@ -562,14 +679,18 @@ bool InitializeAccessibilityTreeSearch( browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS); ui::AXNameFrom nameFrom = static_cast( browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM)); + std::string name = browserAccessibility_->GetStringAttribute( + ui::AX_ATTR_NAME); + + // VoiceOver ignores titleUIElement on non-control AX nodes, so this special + // case expressly returns a nonempty text name for these nodes. if (nameFrom == ui::AX_NAME_FROM_RELATED_ELEMENT && labelledby_ids.size() == 1 && - browserAccessibility_->manager()->GetFromID(labelledby_ids[0])) { - return @""; + browserAccessibility_->manager()->GetFromID(labelledby_ids[0]) && + !browserAccessibility_->IsControl()) { + return base::SysUTF8ToNSString(name); } - std::string name = browserAccessibility_->GetStringAttribute( - ui::AX_ATTR_NAME); if (!name.empty()) { // On Mac OS X, the accessible name of an object is exposed as its // title if it comes from visible text, and as its description @@ -620,6 +741,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSNumber*)disclosing { + if (![self instanceActive]) + return nil; if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) { return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)]; @@ -629,12 +752,17 @@ bool InitializeAccessibilityTreeSearch( } - (id)disclosedByRow { + if (![self instanceActive]) + return nil; + // The row that contains this row. // It should be the same as the first parent that is a treeitem. return nil; } - (NSNumber*)disclosureLevel { + if (![self instanceActive]) + return nil; ui::AXRole role = [self internalRole]; if (role == ui::AX_ROLE_ROW || role == ui::AX_ROLE_TREE_ITEM) { @@ -650,36 +778,78 @@ bool InitializeAccessibilityTreeSearch( } - (id)disclosedRows { + if (![self instanceActive]) + return nil; + // The rows that are considered inside this row. return nil; } -- (NSString*)dropeffect { - std::string dropEffect; - if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffect)) - return base::SysUTF8ToNSString(dropEffect); +- (NSString*)dropEffects { + if (![self instanceActive]) + return nil; + + std::string dropEffects; + if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffects)) + return base::SysUTF8ToNSString(dropEffects); return nil; } - (NSNumber*)enabled { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_ENABLED)]; } +// Returns a text marker that points to the last character in the document that +// can be selected with VoiceOver. +- (id)endTextMarker { + if (![self instanceActive]) + return nil; + + const BrowserAccessibility* root = + browserAccessibility_->manager()->GetRoot(); + if (!root) + return nil; + + const BrowserAccessibility* last_text_object = + root->InternalDeepestLastChild(); + if (last_text_object && !last_text_object->IsTextOnlyObject()) { + last_text_object = + BrowserAccessibilityManager::PreviousTextOnlyObject(last_text_object); + } + while (last_text_object) { + last_text_object = + BrowserAccessibilityManager::PreviousTextOnlyObject(last_text_object); + } + if (!last_text_object) + return nil; + + return CreateTextMarker(*last_text_object, + last_text_object->GetText().length()); +} + - (NSNumber*)expanded { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)]; } - (NSNumber*)focused { + if (![self instanceActive]) + return nil; BrowserAccessibilityManager* manager = browserAccessibility_->manager(); NSNumber* ret = [NSNumber numberWithBool: - manager->GetFocus(NULL) == browserAccessibility_]; + manager->GetFocus() == browserAccessibility_]; return ret; } - (NSNumber*)grabbed { + if (![self instanceActive]) + return nil; std::string grabbed; if (browserAccessibility_->GetHtmlAttribute("aria-grabbed", &grabbed) && grabbed == "true") @@ -689,6 +859,8 @@ bool InitializeAccessibilityTreeSearch( } - (id)header { + if (![self instanceActive]) + return nil; int headerElementId = -1; if ([self internalRole] == ui::AX_ROLE_TABLE || [self internalRole] == ui::AX_ROLE_GRID) { @@ -706,17 +878,21 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* headerObject = browserAccessibility_->manager()->GetFromID(headerElementId); if (headerObject) - return headerObject->ToBrowserAccessibilityCocoa(); + return ToBrowserAccessibilityCocoa(headerObject); } return nil; } - (NSString*)help { + if (![self instanceActive]) + return nil; return NSStringForStringAttribute( browserAccessibility_, ui::AX_ATTR_DESCRIPTION); } - (NSNumber*)index { + if (![self instanceActive]) + return nil; if ([self internalRole] == ui::AX_ROLE_COLUMN) { int columnIndex = browserAccessibility_->GetIntAttribute( ui::AX_ATTR_TABLE_COLUMN_INDEX); @@ -730,13 +906,43 @@ bool InitializeAccessibilityTreeSearch( return nil; } +- (NSNumber*)insertionPointLineNumber { + if (![self instanceActive]) + return nil; + + // TODO(nektar): Deprecate sel_start and sel_end attributes. + int selStart, selEnd; + if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, + &selStart) || + !browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, + &selEnd)) { + return nil; + } + + if (selStart > selEnd) + std::swap(selStart, selEnd); + + const std::vector& line_breaks = + browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LINE_BREAKS); + for (int i = 0; i < static_cast(line_breaks.size()); ++i) { + if (line_breaks[i] > selStart) + return [NSNumber numberWithInt:i]; + } + + return [NSNumber numberWithInt:static_cast(line_breaks.size())]; +} + // Returns whether or not this node should be ignored in the // accessibility tree. - (BOOL)isIgnored { + if (![self instanceActive]) + return YES; return [[self role] isEqualToString:NSAccessibilityUnknownRole]; } - (NSString*)invalid { + if (![self instanceActive]) + return nil; int invalidState; if (!browserAccessibility_->GetIntAttribute( ui::AX_ATTR_INVALID_STATE, &invalidState)) @@ -768,7 +974,16 @@ bool InitializeAccessibilityTreeSearch( return @"false"; } +- (NSNumber*)isMultiSelectable { + if (![self instanceActive]) + return nil; + return [NSNumber numberWithBool:GetState(browserAccessibility_, + ui::AX_STATE_MULTISELECTABLE)]; +} + - (NSString*)placeholderValue { + if (![self instanceActive]) + return nil; ui::AXNameFrom nameFrom = static_cast( browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM)); if (nameFrom == ui::AX_NAME_FROM_PLACEHOLDER) { @@ -787,6 +1002,7 @@ bool InitializeAccessibilityTreeSearch( browserAccessibility_, ui::AX_ATTR_PLACEHOLDER); } +// private - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute addTo:(NSMutableArray*)outArray { const std::vector& attributeValues = @@ -795,10 +1011,11 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* element = browserAccessibility_->manager()->GetFromID(attributeValues[i]); if (element) - [outArray addObject:element->ToBrowserAccessibilityCocoa()]; + [outArray addObject:ToBrowserAccessibilityCocoa(element)]; } } +// private - (NSArray*)linkedUIElements { NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret]; @@ -809,28 +1026,38 @@ bool InitializeAccessibilityTreeSearch( } - (NSNumber*)loaded { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool:YES]; } - (NSNumber*)loadingProgress { + if (![self instanceActive]) + return nil; BrowserAccessibilityManager* manager = browserAccessibility_->manager(); float floatValue = manager->GetTreeData().loading_progress; return [NSNumber numberWithFloat:floatValue]; } - (NSNumber*)maxValue { + if (![self instanceActive]) + return nil; float floatValue = browserAccessibility_->GetFloatAttribute( ui::AX_ATTR_MAX_VALUE_FOR_RANGE); return [NSNumber numberWithFloat:floatValue]; } - (NSNumber*)minValue { + if (![self instanceActive]) + return nil; float floatValue = browserAccessibility_->GetFloatAttribute( ui::AX_ATTR_MIN_VALUE_FOR_RANGE); return [NSNumber numberWithFloat:floatValue]; } - (NSString*)orientation { + if (![self instanceActive]) + return nil; if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)) return NSAccessibilityVerticalOrientationValue; else if (GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) @@ -840,23 +1067,29 @@ bool InitializeAccessibilityTreeSearch( } - (NSNumber*)numberOfCharacters { + if (![self instanceActive]) + return nil; base::string16 value = browserAccessibility_->GetValue(); - return [NSNumber numberWithInt:value.size()]; + return [NSNumber numberWithUnsignedInt:value.size()]; } // The origin of this accessibility object in the page's document. // This is relative to webkit's top-left origin, not Cocoa's // bottom-left origin. - (NSPoint)origin { + if (![self instanceActive]) + return NSMakePoint(0, 0); gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect(); return NSMakePoint(bounds.x(), bounds.y()); } - (id)parent { + if (![self instanceActive]) + return nil; // A nil parent means we're the root. if (browserAccessibility_->GetParent()) { return NSAccessibilityUnignoredAncestor( - browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()); + ToBrowserAccessibilityCocoa(browserAccessibility_->GetParent())); } else { // Hook back up to RenderWidgetHostViewCocoa. BrowserAccessibilityManagerMac* manager = @@ -867,6 +1100,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSValue*)position { + if (![self instanceActive]) + return nil; NSPoint origin = [self origin]; NSSize size = [[self size] sizeValue]; NSPoint pointInScreen = [self pointInScreen:origin size:size]; @@ -874,17 +1109,21 @@ bool InitializeAccessibilityTreeSearch( } - (NSNumber*)required { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)]; } // Returns an enum indicating the role from browserAccessibility_. +// internal - (ui::AXRole)internalRole { return static_cast(browserAccessibility_->GetRole()); } // Returns true if this should expose its accessible name in AXValue. -- (bool)shouldExposeNameInAXValue { +// internal +- (BOOL)shouldExposeNameInAXValue { switch ([self internalRole]) { case ui::AX_ROLE_LIST_BOX_OPTION: case ui::AX_ROLE_LIST_MARKER: @@ -896,19 +1135,24 @@ bool InitializeAccessibilityTreeSearch( } } +// internal - (content::BrowserAccessibilityDelegate*)delegate { - return browserAccessibility_->manager() ? - browserAccessibility_->manager()->delegate() : - nil; + return [self instanceActive] ? browserAccessibility_->manager()->delegate() + : nil; } - (content::BrowserAccessibility*)browserAccessibility { return browserAccessibility_; } +- (BOOL)instanceActive { + return browserAccessibility_ && browserAccessibility_->instance_active(); +} + +// internal - (NSPoint)pointInScreen:(NSPoint)origin size:(NSSize)size { - if (!browserAccessibility_) + if (![self instanceActive]) return NSZeroPoint; // Get the delegate for the topmost BrowserAccessibilityManager, because @@ -926,7 +1170,7 @@ bool InitializeAccessibilityTreeSearch( // Returns a string indicating the NSAccessibility role of this object. - (NSString*)role { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; ui::AXRole role = [self internalRole]; @@ -963,6 +1207,8 @@ bool InitializeAccessibilityTreeSearch( // Returns a string indicating the role description of this object. - (NSString*)roleDescription { + if (![self instanceActive]) + return nil; NSString* role = [self role]; ContentClient* content_client = content::GetContentClient(); @@ -1013,13 +1259,13 @@ bool InitializeAccessibilityTreeSearch( IDS_AX_ROLE_COMPLEMENTARY)); case ui::AX_ROLE_CONTENT_INFO: return base::SysUTF16ToNSString(content_client->GetLocalizedString( - IDS_AX_ROLE_ADDRESS)); + IDS_AX_ROLE_CONTENT_INFO)); case ui::AX_ROLE_DESCRIPTION_LIST: return base::SysUTF16ToNSString(content_client->GetLocalizedString( IDS_AX_ROLE_DESCRIPTION_LIST)); case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: return base::SysUTF16ToNSString(content_client->GetLocalizedString( - IDS_AX_ROLE_DESCRIPTION_DETAIL)); + IDS_AX_ROLE_DEFINITION)); case ui::AX_ROLE_DESCRIPTION_LIST_TERM: return base::SysUTF16ToNSString(content_client->GetLocalizedString( IDS_AX_ROLE_DESCRIPTION_TERM)); @@ -1071,6 +1317,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSArray*)rowHeaders { + if (![self instanceActive]) + return nil; if ([self internalRole] != ui::AX_ROLE_TABLE && [self internalRole] != ui::AX_ROLE_GRID) { return nil; @@ -1084,12 +1332,14 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromID(id); if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) - [ret addObject:cell->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(cell)]; } return ret; } - (NSValue*)rowIndexRange { + if (![self instanceActive]) + return nil; if (!browserAccessibility_->IsCellOrTableHeaderRole()) return nil; @@ -1105,6 +1355,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSArray*)rows { + if (![self instanceActive]) + return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; if ([self internalRole] == ui::AX_ROLE_TABLE|| @@ -1122,24 +1374,35 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* rowElement = browserAccessibility_->manager()->GetFromID(id); if (rowElement) - [ret addObject:rowElement->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(rowElement)]; } } return ret; } +- (NSNumber*)selected { + if (![self instanceActive]) + return nil; + // TODO(nektar): Implement. + return [NSNumber numberWithBool:NO]; +} + - (NSArray*)selectedChildren { + if (![self instanceActive]) + return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; BrowserAccessibilityManager* manager = browserAccessibility_->manager(); - BrowserAccessibility* focusedChild = manager->GetFocus(browserAccessibility_); + BrowserAccessibility* focusedChild = manager->GetFocus(); + if (!focusedChild->IsDescendantOf(browserAccessibility_)) + focusedChild = nullptr; // If it's not multiselectable, try to skip iterating over the // children. if (!GetState(browserAccessibility_, ui::AX_STATE_MULTISELECTABLE)) { // First try the focused child. if (focusedChild && focusedChild != browserAccessibility_) { - [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)]; return ret; } @@ -1150,7 +1413,7 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* activeDescendant = manager->GetFromID(activeDescendantId); if (activeDescendant) { - [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(activeDescendant)]; return ret; } } @@ -1164,49 +1427,149 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(index); if (child->HasState(ui::AX_STATE_SELECTED)) - [ret addObject:child->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(child)]; } // And if nothing's selected but one has focus, use the focused one. if ([ret count] == 0 && focusedChild && focusedChild != browserAccessibility_) { - [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)]; } return ret; } -// Returns the size of this object. +- (NSString*)selectedText { + if (![self instanceActive]) + return nil; + + // TODO(nektar): Deprecate sel_start and sel_end attributes. + int selStart, selEnd; + if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, + &selStart) || + !browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, + &selEnd)) { + return nil; + } + + if (selStart > selEnd) + std::swap(selStart, selEnd); + + int selLength = selEnd - selStart; + base::string16 value = browserAccessibility_->GetValue(); + return base::SysUTF16ToNSString(value.substr(selStart, selLength)); +} + +- (NSValue*)selectedTextRange { + if (![self instanceActive]) + return nil; + + // TODO(nektar): Deprecate sel_start and sel_end attributes. + int selStart, selEnd; + if (!browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, + &selStart) || + !browserAccessibility_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, + &selEnd)) { + return nil; + } + + if (selStart > selEnd) + std::swap(selStart, selEnd); + + int selLength = selEnd - selStart; + return [NSValue valueWithRange:NSMakeRange(selStart, selLength)]; +} + +- (id)selectedTextMarkerRange { + if (![self instanceActive]) + return nil; + + BrowserAccessibilityManager* manager = browserAccessibility_->manager(); + if (!manager) + return nil; + + int32_t anchorId = manager->GetTreeData().sel_anchor_object_id; + const BrowserAccessibility* anchorObject = manager->GetFromID(anchorId); + if (!anchorObject) + return nil; + + int32_t focusId = manager->GetTreeData().sel_focus_object_id; + const BrowserAccessibility* focusObject = manager->GetFromID(focusId); + if (!focusObject) + return nil; + + int anchorOffset = manager->GetTreeData().sel_anchor_offset; + int focusOffset = manager->GetTreeData().sel_focus_offset; + if (anchorOffset < 0 || focusOffset < 0) + return nil; + + return CreateTextMarkerRange(*anchorObject, anchorOffset, *focusObject, + focusOffset); +} + - (NSValue*)size { + if (![self instanceActive]) + return nil; gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect(); return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())]; } - (NSString*)sortDirection { + if (![self instanceActive]) + return nil; int sortDirection; if (!browserAccessibility_->GetIntAttribute( ui::AX_ATTR_SORT_DIRECTION, &sortDirection)) - return @""; + return nil; switch (sortDirection) { case ui::AX_SORT_DIRECTION_UNSORTED: - return @""; + return nil; case ui::AX_SORT_DIRECTION_ASCENDING: - return @"AXSortDirectionAscending"; + return NSAccessibilityAscendingSortDirectionValue; case ui::AX_SORT_DIRECTION_DESCENDING: - return @"AXSortDirectionDescending"; + return NSAccessibilityDescendingSortDirectionValue; case ui::AX_SORT_DIRECTION_OTHER: - return @"AXSortDirectionUnknown"; + return NSAccessibilityUnknownSortDirectionValue; default: NOTREACHED(); } - return @""; + return nil; +} + +// Returns a text marker that points to the first character in the document that +// can be selected with VoiceOver. +- (id)startTextMarker { + if (![self instanceActive]) + return nil; + + const BrowserAccessibility* root = + browserAccessibility_->manager()->GetRoot(); + if (!root) + return nil; + + const BrowserAccessibility* first_text_object = + root->InternalDeepestFirstChild(); + if (first_text_object && !first_text_object->IsTextOnlyObject()) { + first_text_object = + BrowserAccessibilityManager::NextTextOnlyObject(first_text_object); + } + while (first_text_object) { + first_text_object = + BrowserAccessibilityManager::NextTextOnlyObject(first_text_object); + } + if (!first_text_object) + return nil; + + return CreateTextMarker(*first_text_object, 0); } // Returns a subrole based upon the role. - (NSString*) subrole { + if (![self instanceActive]) + return nil; ui::AXRole browserAccessibilityRole = [self internalRole]; if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD && GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) { @@ -1224,6 +1587,8 @@ bool InitializeAccessibilityTreeSearch( // Returns all tabs in this subtree. - (NSArray*)tabs { + if (![self instanceActive]) + return nil; NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease]; if ([self internalRole] == ui::AX_ROLE_TAB) @@ -1239,6 +1604,8 @@ bool InitializeAccessibilityTreeSearch( } - (NSString*)title { + if (![self instanceActive]) + return nil; // Mac OS X wants static text exposed in AXValue. if ([self shouldExposeNameInAXValue]) return @""; @@ -1269,6 +1636,8 @@ bool InitializeAccessibilityTreeSearch( } - (id)titleUIElement { + if (![self instanceActive]) + return nil; std::vector labelledby_ids = browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS); ui::AXNameFrom nameFrom = static_cast( @@ -1278,13 +1647,15 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* titleElement = browserAccessibility_->manager()->GetFromID(labelledby_ids[0]); if (titleElement) - return titleElement->ToBrowserAccessibilityCocoa(); + return ToBrowserAccessibilityCocoa(titleElement); } return nil; } - (NSURL*)url { + if (![self instanceActive]) + return nil; std::string url; if ([[self role] isEqualToString:@"AXWebArea"]) url = browserAccessibility_->manager()->GetTreeData().url; @@ -1298,6 +1669,8 @@ bool InitializeAccessibilityTreeSearch( } - (id)value { + if (![self instanceActive]) + return nil; NSString* role = [self role]; if ([self shouldExposeNameInAXValue]) { return NSStringForStringAttribute( @@ -1347,11 +1720,11 @@ bool InitializeAccessibilityTreeSearch( return [NSNumber numberWithFloat:floatValue]; } } else if ([role isEqualToString:NSAccessibilityColorWellRole]) { - int color = browserAccessibility_->GetIntAttribute( - ui::AX_ATTR_COLOR_VALUE); - int red = (color >> 16) & 0xFF; - int green = (color >> 8) & 0xFF; - int blue = color & 0xFF; + unsigned int color = static_cast( + browserAccessibility_->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); + unsigned int red = SkColorGetR(color); + unsigned int green = SkColorGetG(color); + unsigned int blue = SkColorGetB(color); // This string matches the one returned by a native Mac color well. return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1", red / 255., green / 255., blue / 255.]; @@ -1361,17 +1734,23 @@ bool InitializeAccessibilityTreeSearch( } - (NSString*)valueDescription { + if (![self instanceActive]) + return nil; if (browserAccessibility_) return base::SysUTF16ToNSString(browserAccessibility_->GetValue()); return nil; } - (NSValue*)visibleCharacterRange { + if (![self instanceActive]) + return nil; base::string16 value = browserAccessibility_->GetValue(); return [NSValue valueWithRange:NSMakeRange(0, value.size())]; } - (NSArray*)visibleCells { + if (![self instanceActive]) + return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; const std::vector& uniqueCellIds = browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS); @@ -1380,38 +1759,45 @@ bool InitializeAccessibilityTreeSearch( BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromID(id); if (cell) - [ret addObject:cell->ToBrowserAccessibilityCocoa()]; + [ret addObject:ToBrowserAccessibilityCocoa(cell)]; } return ret; } - (NSArray*)visibleChildren { + if (![self instanceActive]) + return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; uint32_t childCount = browserAccessibility_->PlatformChildCount(); for (uint32_t index = 0; index < childCount; ++index) { - BrowserAccessibilityCocoa* child = - browserAccessibility_->PlatformGetChild(index)-> - ToBrowserAccessibilityCocoa(); + BrowserAccessibilityCocoa* child = ToBrowserAccessibilityCocoa( + browserAccessibility_->PlatformGetChild(index)); [ret addObject:child]; } return ret; } - (NSArray*)visibleColumns { + if (![self instanceActive]) + return nil; return [self columns]; } - (NSArray*)visibleRows { + if (![self instanceActive]) + return nil; return [self rows]; } - (NSNumber*)visited { + if (![self instanceActive]) + return nil; return [NSNumber numberWithBool: GetState(browserAccessibility_, ui::AX_STATE_VISITED)]; } - (id)window { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; BrowserAccessibilityManagerMac* manager = @@ -1433,7 +1819,7 @@ bool InitializeAccessibilityTreeSearch( // Returns the requested text range from this object's value attribute. - (NSString*)valueForRange:(NSRange)range { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; base::string16 value = browserAccessibility_->GetValue(); @@ -1446,7 +1832,7 @@ bool InitializeAccessibilityTreeSearch( // Returns the accessibility value for the given attribute. If the value isn't // supported this will return nil. - (id)accessibilityAttributeValue:(NSString*)attribute { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; SEL selector = @@ -1454,41 +1840,15 @@ bool InitializeAccessibilityTreeSearch( if (selector) return [self performSelector:selector]; - // TODO(dtseng): refactor remaining attributes. - int selStart, selEnd; - if (browserAccessibility_->GetIntAttribute( - ui::AX_ATTR_TEXT_SEL_START, &selStart) && - browserAccessibility_-> - GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) { - if (selStart > selEnd) - std::swap(selStart, selEnd); - int selLength = selEnd - selStart; - if ([attribute isEqualToString: - NSAccessibilityInsertionPointLineNumberAttribute]) { - const std::vector& line_breaks = - browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LINE_BREAKS); - for (int i = 0; i < static_cast(line_breaks.size()); ++i) { - if (line_breaks[i] > selStart) - return [NSNumber numberWithInt:i]; - } - return [NSNumber numberWithInt:static_cast(line_breaks.size())]; - } - if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { - base::string16 value = browserAccessibility_->GetValue(); - return base::SysUTF16ToNSString(value.substr(selStart, selLength)); - } - if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { - return [NSValue valueWithRange:NSMakeRange(selStart, selLength)]; - } - } return nil; } // Returns the accessibility value for the given attribute and parameter. If the // value isn't supported this will return nil. +// TODO(nektar): Implement all unimplemented attributes, e.g. text markers. - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; const std::vector& line_breaks = @@ -1577,7 +1937,7 @@ bool InitializeAccessibilityTreeSearch( continue; } if (colIndex == column) - return cell->ToBrowserAccessibilityCocoa(); + return ToBrowserAccessibilityCocoa(cell); if (colIndex > column) break; } @@ -1585,6 +1945,96 @@ bool InitializeAccessibilityTreeSearch( return nil; } + if ([attribute isEqualToString:@"AXUIElementForTextMarker"]) { + BrowserAccessibility* object; + int offset; + if (GetTextMarkerData(parameter, &object, &offset)) + return ToBrowserAccessibilityCocoa(object); + + return nil; + } + + if ([attribute isEqualToString:@"AXTextMarkerRangeForUIElement"]) { + return CreateTextMarkerRange(*browserAccessibility_, 0, + *browserAccessibility_, + browserAccessibility_->GetText().length()); + } + + if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) + return GetTextForTextMarkerRange(parameter); + + if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"]) { + NSString* text = GetTextForTextMarkerRange(parameter); + return [[[NSAttributedString alloc] initWithString:text] autorelease]; + } + + if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) { + BrowserAccessibility* object; + int offset; + if (!GetTextMarkerData(parameter, &object, &offset)) + return nil; + + DCHECK(object); + if ((object->IsSimpleTextControl() || object->IsTextOnlyObject()) && + offset < static_cast(object->GetText().length())) { + ++offset; + } else { + do { + object = BrowserAccessibilityManager::NextTextOnlyObject(object); + } while ( + object && + !(object->IsTextOnlyObject() && object->GetText().length() == 0)); + if (!object) + return nil; + + offset = 0; + } + + return CreateTextMarker(*object, offset); + } + + if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) { + BrowserAccessibility* object; + int offset; + if (!GetTextMarkerData(parameter, &object, &offset)) + return nil; + + DCHECK(object); + if ((object->IsSimpleTextControl() || object->IsTextOnlyObject()) && + offset > 0) { + --offset; + } else { + do { + object = BrowserAccessibilityManager::PreviousTextOnlyObject(object); + } while ( + object && + !(object->IsTextOnlyObject() && object->GetText().length() == 0)); + if (!object) + return nil; + + offset = object->GetText().length() - 1; + } + + return CreateTextMarker(*object, offset); + } + + if ([attribute + isEqualToString:@"AXPreviousWordStartTextMarkerForTextMarker"]) { + BrowserAccessibility* object; + int offset; + if (!GetTextMarkerData(parameter, &object, &offset)) + return nil; + + DCHECK(object); + offset = object->GetWordStartBoundary(offset, ui::BACKWARDS_DIRECTION); + return CreateTextMarker(*object, offset); + } + + if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) { + NSString* text = GetTextForTextMarkerRange(parameter); + return [NSNumber numberWithInt:[text length]]; + } + if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) { if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT) @@ -1599,6 +2049,7 @@ bool InitializeAccessibilityTreeSearch( pointInScreen.x, pointInScreen.y, rect.width(), rect.height()); return [NSValue valueWithRect:nsrect]; } + if ([attribute isEqualToString:@"AXUIElementCountForSearchPredicate"]) { OneShotAccessibilityTreeSearch search(browserAccessibility_); if (InitializeAccessibilityTreeSearch(&search, parameter)) @@ -1613,63 +2064,90 @@ bool InitializeAccessibilityTreeSearch( NSMutableArray* result = [NSMutableArray arrayWithCapacity:count]; for (size_t i = 0; i < count; ++i) { BrowserAccessibility* match = search.GetMatchAtIndex(i); - [result addObject:match->ToBrowserAccessibilityCocoa()]; + [result addObject:ToBrowserAccessibilityCocoa(match)]; } return result; } return nil; } - // TODO(dtseng): support the following attributes. - if ([attribute isEqualTo: - NSAccessibilityRangeForPositionParameterizedAttribute] || - [attribute isEqualTo: - NSAccessibilityRangeForIndexParameterizedAttribute] || - [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] || - [attribute isEqualTo: - NSAccessibilityStyleRangeForIndexParameterizedAttribute]) { - return nil; - } return nil; } // Returns an array of parameterized attributes names that this object will // respond to. - (NSArray*)accessibilityParameterizedAttributeNames { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; // General attributes. - NSMutableArray* ret = [NSMutableArray arrayWithObjects: - @"AXUIElementCountForSearchPredicate", - @"AXUIElementsForSearchPredicate", - nil]; + NSMutableArray* ret = [NSMutableArray + arrayWithObjects: + @"AXUIElementForTextMarker", @"AXTextMarkerRangeForUIElement", + @"AXLineForTextMarker", @"AXTextMarkerRangeForLine", + @"AXStringForTextMarkerRange", @"AXTextMarkerForPosition", + @"AXBoundsForTextMarkerRange", + @"AXAttributedStringForTextMarkerRange", + @"AXTextMarkerRangeForUnorderedTextMarkers", + @"AXNextTextMarkerForTextMarker", + @"AXPreviousTextMarkerForTextMarker", + @"AXLeftWordTextMarkerRangeForTextMarker", + @"AXRightWordTextMarkerRangeForTextMarker", + @"AXLeftLineTextMarkerRangeForTextMarker", + @"AXRightLineTextMarkerRangeForTextMarker", + @"AXSentenceTextMarkerRangeForTextMarker", + @"AXParagraphTextMarkerRangeForTextMarker", + @"AXNextWordEndTextMarkerForTextMarker", + @"AXPreviousWordStartTextMarkerForTextMarker", + @"AXNextLineEndTextMarkerForTextMarker", + @"AXPreviousLineStartTextMarkerForTextMarker", + @"AXNextSentenceEndTextMarkerForTextMarker", + @"AXPreviousSentenceStartTextMarkerForTextMarker", + @"AXNextParagraphEndTextMarkerForTextMarker", + @"AXPreviousParagraphStartTextMarkerForTextMarker", + @"AXStyleTextMarkerRangeForTextMarker", @"AXLengthForTextMarkerRange", + NSAccessibilityBoundsForRangeParameterizedAttribute, + NSAccessibilityStringForRangeParameterizedAttribute, + NSAccessibilityUIElementCountForSearchPredicateParameterizedAttribute, + NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute, + NSAccessibilityEndTextMarkerForBoundsParameterizedAttribute, + NSAccessibilityStartTextMarkerForBoundsParameterizedAttribute, + NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute, + NSAccessibilitySelectTextWithCriteriaParameterizedAttribute, nil]; if ([[self role] isEqualToString:NSAccessibilityTableRole] || [[self role] isEqualToString:NSAccessibilityGridRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityCellForColumnAndRowParameterizedAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityCellForColumnAndRowParameterizedAttribute + ]]; } if (browserAccessibility_->HasState(ui::AX_STATE_EDITABLE)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityLineForIndexParameterizedAttribute, - NSAccessibilityRangeForLineParameterizedAttribute, - NSAccessibilityStringForRangeParameterizedAttribute, - NSAccessibilityRangeForPositionParameterizedAttribute, - NSAccessibilityRangeForIndexParameterizedAttribute, - NSAccessibilityBoundsForRangeParameterizedAttribute, - NSAccessibilityRTFForRangeParameterizedAttribute, - NSAccessibilityAttributedStringForRangeParameterizedAttribute, - NSAccessibilityStyleRangeForIndexParameterizedAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityLineForIndexParameterizedAttribute, + NSAccessibilityRangeForLineParameterizedAttribute, + NSAccessibilityStringForRangeParameterizedAttribute, + NSAccessibilityRangeForPositionParameterizedAttribute, + NSAccessibilityRangeForIndexParameterizedAttribute, + NSAccessibilityBoundsForRangeParameterizedAttribute, + NSAccessibilityRTFForRangeParameterizedAttribute, + NSAccessibilityAttributedStringForRangeParameterizedAttribute, + NSAccessibilityStyleRangeForIndexParameterizedAttribute + ]]; } if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityBoundsForRangeParameterizedAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityBoundsForRangeParameterizedAttribute + ]]; + } + + if ([self internalRole] == ui::AX_ROLE_ROOT_WEB_AREA || + [self internalRole] == ui::AX_ROLE_WEB_AREA) { + [ret addObjectsFromArray: @[ + NSAccessibilityTextMarkerIsValidParameterizedAttribute, + NSAccessibilityIndexForTextMarkerParameterizedAttribute, + NSAccessibilityTextMarkerForIndexParameterizedAttribute]]; } return ret; @@ -1677,20 +2155,27 @@ bool InitializeAccessibilityTreeSearch( // Returns an array of action names that this object will respond to. - (NSArray*)accessibilityActionNames { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; - NSMutableArray* ret = - [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction]; - NSString* role = [self role]; - // TODO(dtseng): this should only get set when there's a default action. - if (![role isEqualToString:NSAccessibilityStaticTextRole] && - ![role isEqualToString:NSAccessibilityTextFieldRole] && - ![role isEqualToString:NSAccessibilityTextAreaRole]) { - [ret addObject:NSAccessibilityPressAction]; + NSMutableArray* actions = [NSMutableArray + arrayWithObjects:NSAccessibilityShowMenuAction, + NSAccessibilityScrollToVisibleAction, nil]; + + // VoiceOver expects the "press" action to be first. + if (browserAccessibility_->IsClickable()) + [actions insertObject:NSAccessibilityPressAction atIndex:0]; + + if (browserAccessibility_->IsMenuRelated()) + [actions addObject:NSAccessibilityCancelAction]; + + if ([self internalRole] == ui::AX_ROLE_SLIDER) { + [actions addObjectsFromArray:@[ + NSAccessibilityIncrementAction, NSAccessibilityDecrementAction + ]]; } - return ret; + return actions; } // Returns a sub-array of values for the given attribute value, starting at @@ -1702,7 +2187,7 @@ bool InitializeAccessibilityTreeSearch( - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; NSArray* fullArray = [self accessibilityAttributeValue:attribute]; @@ -1722,7 +2207,7 @@ bool InitializeAccessibilityTreeSearch( // Returns the count of the specified accessibility array attribute. - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute { - if (!browserAccessibility_) + if (![self instanceActive]) return 0; NSArray* fullArray = [self accessibilityAttributeValue:attribute]; @@ -1731,83 +2216,78 @@ bool InitializeAccessibilityTreeSearch( // Returns the list of accessibility attributes that this object supports. - (NSArray*)accessibilityAttributeNames { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; // General attributes. - NSMutableArray* ret = [NSMutableArray arrayWithObjects: - NSAccessibilityChildrenAttribute, - NSAccessibilityDescriptionAttribute, - NSAccessibilityEnabledAttribute, - NSAccessibilityFocusedAttribute, - NSAccessibilityHelpAttribute, - NSAccessibilityLinkedUIElementsAttribute, - NSAccessibilityParentAttribute, - NSAccessibilityPositionAttribute, - NSAccessibilityRoleAttribute, - NSAccessibilityRoleDescriptionAttribute, - NSAccessibilitySizeAttribute, - NSAccessibilitySubroleAttribute, - NSAccessibilityTitleAttribute, - NSAccessibilityTopLevelUIElementAttribute, - NSAccessibilityValueAttribute, - NSAccessibilityWindowAttribute, - @"AXAccessKey", - @"AXInvalid", - @"AXVisited", - nil]; + NSMutableArray* ret = [NSMutableArray + arrayWithObjects:NSAccessibilityAccessKeyAttribute, + NSAccessibilityChildrenAttribute, + NSAccessibilityDescriptionAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilityEndTextMarkerAttribute, + NSAccessibilityFocusedAttribute, + NSAccessibilityHelpAttribute, + NSAccessibilityInvalidAttribute, + NSAccessibilityLinkedUIElementsAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilityRoleAttribute, + NSAccessibilityRoleDescriptionAttribute, + NSAccessibilitySelectedTextMarkerRangeAttribute, + NSAccessibilitySizeAttribute, + NSAccessibilityStartTextMarkerAttribute, + NSAccessibilitySubroleAttribute, + NSAccessibilityTitleAttribute, + NSAccessibilityTitleUIElementAttribute, + NSAccessibilityTopLevelUIElementAttribute, + NSAccessibilityValueAttribute, + NSAccessibilityVisitedAttribute, + NSAccessibilityWindowAttribute, nil]; // Specific role attributes. NSString* role = [self role]; NSString* subrole = [self subrole]; if ([role isEqualToString:NSAccessibilityTableRole] || [role isEqualToString:NSAccessibilityGridRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityColumnsAttribute, - NSAccessibilityVisibleColumnsAttribute, - NSAccessibilityRowsAttribute, - NSAccessibilityVisibleRowsAttribute, - NSAccessibilityVisibleCellsAttribute, - NSAccessibilityHeaderAttribute, - NSAccessibilityColumnHeaderUIElementsAttribute, - NSAccessibilityRowHeaderUIElementsAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityColumnsAttribute, NSAccessibilityVisibleColumnsAttribute, + NSAccessibilityRowsAttribute, NSAccessibilityVisibleRowsAttribute, + NSAccessibilityVisibleCellsAttribute, NSAccessibilityHeaderAttribute, + NSAccessibilityColumnHeaderUIElementsAttribute, + NSAccessibilityRowHeaderUIElementsAttribute + ]]; } else if ([role isEqualToString:NSAccessibilityColumnRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityIndexAttribute, - NSAccessibilityHeaderAttribute, - NSAccessibilityRowsAttribute, - NSAccessibilityVisibleRowsAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityIndexAttribute, NSAccessibilityHeaderAttribute, + NSAccessibilityRowsAttribute, NSAccessibilityVisibleRowsAttribute + ]]; } else if ([role isEqualToString:NSAccessibilityCellRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityColumnIndexRangeAttribute, - NSAccessibilityRowIndexRangeAttribute, - @"AXSortDirection", - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityColumnIndexRangeAttribute, + NSAccessibilityRowIndexRangeAttribute, @"AXSortDirection" + ]]; } else if ([role isEqualToString:@"AXWebArea"]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXLoaded", - @"AXLoadingProgress", - nil]]; + [ret addObjectsFromArray:@[ + @"AXLoaded", NSAccessibilityLoadingProgressAttribute + ]]; } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) { [ret addObject:NSAccessibilityTabsAttribute]; } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] || [role isEqualToString:NSAccessibilitySliderRole] || [role isEqualToString:NSAccessibilityIncrementorRole] || [role isEqualToString:NSAccessibilityScrollBarRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityMaxValueAttribute, - NSAccessibilityMinValueAttribute, - NSAccessibilityValueDescriptionAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityMaxValueAttribute, NSAccessibilityMinValueAttribute, + NSAccessibilityValueDescriptionAttribute + ]]; } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityDisclosingAttribute, - NSAccessibilityDisclosedByRowAttribute, - NSAccessibilityDisclosureLevelAttribute, - NSAccessibilityDisclosedRowsAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityDisclosingAttribute, + NSAccessibilityDisclosedByRowAttribute, + NSAccessibilityDisclosureLevelAttribute, + NSAccessibilityDisclosedRowsAttribute + ]]; } else if ([role isEqualToString:NSAccessibilityRowRole]) { if (browserAccessibility_->GetParent()) { base::string16 parentRole; @@ -1815,126 +2295,99 @@ bool InitializeAccessibilityTreeSearch( "role", &parentRole); const base::string16 treegridRole(base::ASCIIToUTF16("treegrid")); if (parentRole == treegridRole) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityDisclosingAttribute, - NSAccessibilityDisclosedByRowAttribute, - NSAccessibilityDisclosureLevelAttribute, - NSAccessibilityDisclosedRowsAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityDisclosingAttribute, + NSAccessibilityDisclosedByRowAttribute, + NSAccessibilityDisclosureLevelAttribute, + NSAccessibilityDisclosedRowsAttribute + ]]; } else { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityIndexAttribute, - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityIndexAttribute ]]; } } } else if ([role isEqualToString:NSAccessibilityListRole]) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilitySelectedChildrenAttribute, - NSAccessibilityVisibleChildrenAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilitySelectedChildrenAttribute, + NSAccessibilityVisibleChildrenAttribute + ]]; } // Caret navigation and text selection attributes. if (browserAccessibility_->HasState(ui::AX_STATE_EDITABLE)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityInsertionPointLineNumberAttribute, - NSAccessibilityNumberOfCharactersAttribute, - NSAccessibilitySelectedTextAttribute, - NSAccessibilitySelectedTextRangeAttribute, - NSAccessibilityVisibleCharacterRangeAttribute, - nil]]; + [ret addObjectsFromArray:@[ + NSAccessibilityInsertionPointLineNumberAttribute, + NSAccessibilityNumberOfCharactersAttribute, + NSAccessibilitySelectedTextAttribute, + NSAccessibilitySelectedTextRangeAttribute, + NSAccessibilityVisibleCharacterRangeAttribute + ]]; } // Add the url attribute only if it has a valid url. if ([self url] != nil) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityURLAttribute, - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityURLAttribute ]]; } // Position in set and Set size if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_POS_IN_SET)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIAPosInSet", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIAPosInSetAttribute ]]; } if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_SET_SIZE)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIASetSize", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIASetSizeAttribute ]]; } // Live regions. if (browserAccessibility_->HasStringAttribute( ui::AX_ATTR_LIVE_STATUS)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIALive", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIALiveAttribute ]]; } if (browserAccessibility_->HasStringAttribute( ui::AX_ATTR_LIVE_RELEVANT)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIARelevant", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIARelevantAttribute ]]; } if (browserAccessibility_->HasBoolAttribute( ui::AX_ATTR_LIVE_ATOMIC)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIAAtomic", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIAAtomicAttribute ]]; } if (browserAccessibility_->HasBoolAttribute( ui::AX_ATTR_LIVE_BUSY)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXARIABusy", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityARIABusyAttribute ]]; } std::string dropEffect; if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffect)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXDropEffects", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityDropEffectsAttribute ]]; } std::string grabbed; if (browserAccessibility_->GetHtmlAttribute("aria-grabbed", &grabbed)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXGrabbed", - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityGrabbedAttribute ]]; } // Add expanded attribute only if it has expanded or collapsed state. if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) || GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityExpandedAttribute, - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityExpandedAttribute ]]; } if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL) || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityOrientationAttribute, nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityOrientationAttribute ]]; } if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityPlaceholderValueAttribute, nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityPlaceholderValueAttribute ]]; } if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - @"AXRequired", nil]]; + [ret addObjectsFromArray:@[ @"AXRequired" ]]; } // Title UI Element. if (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) && browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) .size() > 0) { - [ret addObjectsFromArray:[NSArray arrayWithObjects: - NSAccessibilityTitleUIElementAttribute, - nil]]; + [ret addObjectsFromArray:@[ NSAccessibilityTitleUIElementAttribute ]]; } // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute // for elements which are referred to by labelledby or are labels @@ -1944,7 +2397,7 @@ bool InitializeAccessibilityTreeSearch( // Returns the index of the child in this objects array of children. - (NSUInteger)accessibilityGetIndexOf:(id)child { - if (!browserAccessibility_) + if (![self instanceActive]) return 0; NSUInteger index = 0; @@ -1959,7 +2412,7 @@ bool InitializeAccessibilityTreeSearch( // Returns whether or not the specified attribute can be set by the // accessibility API via |accessibilitySetValue:forAttribute:|. - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute { - if (!browserAccessibility_) + if (![self instanceActive]) return NO; if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { @@ -1985,7 +2438,7 @@ bool InitializeAccessibilityTreeSearch( // Returns whether or not this object should be ignored in the accessibility // tree. - (BOOL)accessibilityIsIgnored { - if (!browserAccessibility_) + if (![self instanceActive]) return YES; return [self isIgnored]; @@ -1994,7 +2447,7 @@ bool InitializeAccessibilityTreeSearch( // Performs the given accessibility action on the webkit accessibility object // that backs this object. - (void)accessibilityPerformAction:(NSString*)action { - if (!browserAccessibility_) + if (![self instanceActive]) return; // TODO(dmazzoni): Support more actions. @@ -2009,7 +2462,7 @@ bool InitializeAccessibilityTreeSearch( // Returns the description of the given action. - (NSString*)accessibilityActionDescription:(NSString*)action { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; return NSAccessibilityActionDescription(action); @@ -2019,12 +2472,14 @@ bool InitializeAccessibilityTreeSearch( // This class does not support this. - (BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString*)attribute { + if (![self instanceActive]) + return NO; return NO; } // Sets the value for an accessibility attribute via the accessibility API. - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { - if (!browserAccessibility_) + if (![self instanceActive]) return; if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { @@ -2032,7 +2487,7 @@ bool InitializeAccessibilityTreeSearch( NSNumber* focusedNumber = value; BOOL focused = [focusedNumber intValue]; if (focused) - manager->SetFocus(browserAccessibility_, true); + manager->SetFocus(*browserAccessibility_); } if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { NSRange range = [(NSValue*)value rangeValue]; @@ -2047,7 +2502,7 @@ bool InitializeAccessibilityTreeSearch( // or one of its children, so this will never return nil unless this // object is invalid. - (id)accessibilityHitTest:(NSPoint)point { - if (!browserAccessibility_) + if (![self instanceActive]) return nil; BrowserAccessibilityCocoa* hit = self; @@ -2079,7 +2534,7 @@ bool InitializeAccessibilityTreeSearch( - (NSUInteger)hash { // Potentially called during dealloc. - if (!browserAccessibility_) + if (![self instanceActive]) return [super hash]; return browserAccessibility_->GetId(); } diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.h b/chromium/content/browser/accessibility/browser_accessibility_mac.h index aed8fd49e08..d5160243274 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_mac.h +++ b/chromium/content/browser/accessibility/browser_accessibility_mac.h @@ -11,11 +11,19 @@ #include "base/macros.h" #include "content/browser/accessibility/browser_accessibility.h" +#include "content/common/content_export.h" @class BrowserAccessibilityCocoa; namespace content { +#if __OBJC__ +CONTENT_EXPORT const BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa( + const BrowserAccessibility* obj); +CONTENT_EXPORT BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa( + BrowserAccessibility* obj); +#endif + class BrowserAccessibilityMac : public BrowserAccessibility { public: // BrowserAccessibility overrides. diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_mac.mm index da1f55e9673..34475bdc309 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_mac.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_mac.mm @@ -64,13 +64,17 @@ void BrowserAccessibilityMac::RecreateNativeObject() { } const BrowserAccessibilityCocoa* -BrowserAccessibility::ToBrowserAccessibilityCocoa() const { - return static_cast(this)->native_view(); +ToBrowserAccessibilityCocoa(const BrowserAccessibility* obj) { + DCHECK(obj); + DCHECK(obj->IsNative()); + return static_cast(obj)->native_view(); } -BrowserAccessibilityCocoa* BrowserAccessibility::ToBrowserAccessibilityCocoa() { - return static_cast(this)-> - native_view(); +BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa( + BrowserAccessibility* obj) { + DCHECK(obj); + DCHECK(obj->IsNative()); + return static_cast(obj)->native_view(); } } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm index c85957fab59..0b2511dc09d 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_mac_unittest.mm @@ -8,6 +8,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/accessibility/browser_accessibility_cocoa.h" +#include "content/browser/accessibility/browser_accessibility_mac.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_manager_mac.h" #include "testing/gtest/include/gtest/gtest.h" @@ -53,7 +54,7 @@ class BrowserAccessibilityTest : public ui::CocoaTest { nil, MakeAXTreeUpdate(root, child1, child2), NULL)); - accessibility_.reset([manager_->GetRoot()->ToBrowserAccessibilityCocoa() + accessibility_.reset([ToBrowserAccessibilityCocoa(manager_->GetRoot()) retain]); } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc index 8979f10f0f0..f95f5085c33 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc @@ -43,6 +43,10 @@ using AXTreeIDMap = base::hash_map; base::LazyInstance g_ax_tree_id_map = LAZY_INSTANCE_INITIALIZER; +// A function to call when focus changes, for testing only. +base::LazyInstance g_focus_change_callback_for_testing = + LAZY_INSTANCE_INITIALIZER; + ui::AXTreeUpdate MakeAXTreeUpdate( const ui::AXNodeData& node1, const ui::AXNodeData& node2 /* = ui::AXNodeData() */, @@ -87,11 +91,7 @@ ui::AXTreeUpdate MakeAXTreeUpdate( } BrowserAccessibility* BrowserAccessibilityFactory::Create() { -#if defined(OS_ANDROID) && defined(USE_AURA) - return nullptr; -#else return BrowserAccessibility::Create(); -#endif } BrowserAccessibilityFindInPageInfo::BrowserAccessibilityFindInPageInfo() @@ -127,9 +127,11 @@ BrowserAccessibilityManager::BrowserAccessibilityManager( : delegate_(delegate), factory_(factory), tree_(new ui::AXSerializableTree()), - focus_(NULL), user_is_navigating_away_(false), osk_state_(OSK_ALLOWED), + last_focused_node_(nullptr), + last_focused_manager_(nullptr), + connected_to_parent_tree_node_(false), ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID), parent_node_id_from_parent_tree_(0) { tree_->SetDelegate(this); @@ -142,9 +144,10 @@ BrowserAccessibilityManager::BrowserAccessibilityManager( : delegate_(delegate), factory_(factory), tree_(new ui::AXSerializableTree()), - focus_(NULL), user_is_navigating_away_(false), osk_state_(OSK_ALLOWED), + last_focused_node_(nullptr), + last_focused_manager_(nullptr), ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID), parent_node_id_from_parent_tree_(0) { tree_->SetDelegate(this); @@ -166,9 +169,6 @@ void BrowserAccessibilityManager::Initialize( LOG(FATAL) << tree_->error(); } } - - if (!focus_) - SetFocus(tree_->root(), false); } // static @@ -182,6 +182,49 @@ BrowserAccessibilityManager::GetEmptyDocument() { return update; } +void BrowserAccessibilityManager::FireFocusEventsIfNeeded() { + BrowserAccessibility* focus = GetFocus(); + + // Don't fire focus events if the window itself doesn't have focus. + // Bypass this check if a global focus listener was set up for testing + // so that the test passes whether the window is active or not. + if (g_focus_change_callback_for_testing.Get().is_null()) { + if (delegate_ && !delegate_->AccessibilityViewHasFocus()) + focus = nullptr; + + if (!CanFireEvents()) + focus = nullptr; + } + + // Don't allow the document to be focused if it has no children and + // hasn't finished loading yet. Wait for at least a tiny bit of content, + // or for the document to actually finish loading. + if (focus && + focus == focus->manager()->GetRoot() && + focus->PlatformChildCount() == 0 && + !focus->HasState(ui::AX_STATE_BUSY) && + !focus->manager()->GetTreeData().loaded) { + focus = nullptr; + } + + if (focus && focus != last_focused_node_) + FireFocusEvent(focus); + + last_focused_node_ = focus; + last_focused_manager_ = focus ? focus->manager() : nullptr; +} + +bool BrowserAccessibilityManager::CanFireEvents() { + return true; +} + +void BrowserAccessibilityManager::FireFocusEvent(BrowserAccessibility* node) { + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, node); + + if (!g_focus_change_callback_for_testing.Get().is_null()) + g_focus_change_callback_for_testing.Get().Run(); +} + BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { // tree_ can be null during destruction. if (!tree_) @@ -247,13 +290,15 @@ const ui::AXTreeData& BrowserAccessibilityManager::GetTreeData() { } void BrowserAccessibilityManager::OnWindowFocused() { - if (focus_) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); + if (this == GetRootManager()) + FireFocusEventsIfNeeded(); } void BrowserAccessibilityManager::OnWindowBlurred() { - if (focus_) - NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_)); + if (this == GetRootManager()) { + last_focused_node_ = nullptr; + last_focused_manager_ = nullptr; + } } void BrowserAccessibilityManager::UserIsNavigatingAway() { @@ -273,11 +318,12 @@ void BrowserAccessibilityManager::NavigationFailed() { } void BrowserAccessibilityManager::GotMouseDown() { - if (!focus_) + BrowserAccessibility* focus = GetFocus(); + if (!focus) return; osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT; - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, focus); } bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() { @@ -286,8 +332,6 @@ bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() { void BrowserAccessibilityManager::OnAccessibilityEvents( const std::vector& details) { - bool should_send_initial_focus = false; - // Process all changes to the accessibility tree first. for (uint32_t index = 0; index < details.size(); ++index) { const AXEventNotificationDetails& detail = details[index]; @@ -300,18 +344,31 @@ void BrowserAccessibilityManager::OnAccessibilityEvents( } return; } + } - // Set focus to the root if it's not anywhere else. - if (!focus_) { - SetFocus(tree_->root(), false); - should_send_initial_focus = true; + // If the root's parent is in another accessibility tree but it wasn't + // previously connected, post the proper notifications on the parent. + BrowserAccessibility* parent = GetParentNodeFromParentTree(); + if (parent) { + if (!connected_to_parent_tree_node_) { + parent->OnDataChanged(); + parent->UpdatePlatformAttributes(); + parent->manager()->NotifyAccessibilityEvent( + ui::AX_EVENT_CHILDREN_CHANGED, parent); + connected_to_parent_tree_node_ = true; } + } else { + connected_to_parent_tree_node_ = false; } - if (should_send_initial_focus && NativeViewHasFocus()) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); + // Based on the changes to the tree, first fire focus events if needed. + // Screen readers might not do the right thing if they're not aware of what + // has focus, so always try that first. Nothing will be fired if the window + // itself isn't focused or if focus hasn't changed. + GetRootManager()->FireFocusEventsIfNeeded(); - // Now iterate over the events again and fire the events. + // Now iterate over the events again and fire the events other than focus + // events. for (uint32_t index = 0; index < details.size(); index++) { const AXEventNotificationDetails& detail = details[index]; @@ -324,19 +381,22 @@ void BrowserAccessibilityManager::OnAccessibilityEvents( ui::AXEvent event_type = detail.event_type; if (event_type == ui::AX_EVENT_FOCUS || event_type == ui::AX_EVENT_BLUR) { - SetFocus(node, false); - if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN && osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) osk_state_ = OSK_ALLOWED; - // Don't send a native focus event if the window itself doesn't - // have focus. - if (!NativeViewHasFocus()) + bool is_menu_list_option = + node->data().role == ui::AX_ROLE_MENU_LIST_OPTION; + + // Skip all focus events other than ones on menu list options; + // we've already handled them, above. Menu list options are a weird + // exception because the menu list itself has focus but we need to fire + // focus events on the individual options. + if (!is_menu_list_option) continue; } - // Send the event event to the operating system. + // Fire the native event. NotifyAccessibilityEvent(event_type, GetFromAXNode(node)); } } @@ -349,7 +409,16 @@ void BrowserAccessibilityManager::OnLocationChanges( continue; ui::AXNode* node = obj->node(); node->SetLocation(params[i].new_location); - obj->OnLocationChanged(); + } + SendLocationChangeEvents(params); +} + +void BrowserAccessibilityManager::SendLocationChangeEvents( + const std::vector& params) { + for (size_t i = 0; i < params.size(); ++i) { + BrowserAccessibility* obj = GetFromID(params[i].id); + if (obj) + obj->OnLocationChanged(); } } @@ -367,6 +436,22 @@ void BrowserAccessibilityManager::OnFindInPageResult( ActivateFindInPageResult(request_id); } +void BrowserAccessibilityManager::OnChildFrameHitTestResult( + const gfx::Point& point, + int hit_obj_id) { + BrowserAccessibility* obj = GetFromID(hit_obj_id); + if (!obj || !obj->HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) + return; + + BrowserAccessibilityManager* child_manager = + BrowserAccessibilityManager::FromID( + obj->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); + if (!child_manager || !child_manager->delegate()) + return; + + return child_manager->delegate()->AccessibilityHitTest(point); +} + void BrowserAccessibilityManager::ActivateFindInPageResult( int request_id) { find_in_page_info_.active_request_id = request_id; @@ -391,20 +476,19 @@ void BrowserAccessibilityManager::ActivateFindInPageResult( } BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus( - BrowserAccessibility* root) { - BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root); - if (!node) + BrowserAccessibility* focus) { + if (!focus) return NULL; int active_descendant_id; - if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, - &active_descendant_id)) { + if (focus->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, + &active_descendant_id)) { BrowserAccessibility* active_descendant = - node->manager()->GetFromID(active_descendant_id); + focus->manager()->GetFromID(active_descendant_id); if (active_descendant) return active_descendant; } - return node; + return focus; } bool BrowserAccessibilityManager::NativeViewHasFocus() { @@ -414,39 +498,55 @@ bool BrowserAccessibilityManager::NativeViewHasFocus() { return false; } -BrowserAccessibility* BrowserAccessibilityManager::GetFocus( - BrowserAccessibility* root) { - if (!focus_) - return nullptr; +BrowserAccessibility* BrowserAccessibilityManager::GetFocus() { + BrowserAccessibilityManager* root_manager = GetRootManager(); + if (!root_manager) + root_manager = this; + int32_t focused_tree_id = root_manager->GetTreeData().focused_tree_id; - if (root && !focus_->IsDescendantOf(root->node())) - return nullptr; + BrowserAccessibilityManager* focused_manager = nullptr; + if (focused_tree_id) + focused_manager =BrowserAccessibilityManager::FromID(focused_tree_id); + if (!focused_manager) + focused_manager = root_manager; + + return focused_manager->GetFocusFromThisOrDescendantFrame(); +} + +BrowserAccessibility* +BrowserAccessibilityManager::GetFocusFromThisOrDescendantFrame() { + int32_t focus_id = GetTreeData().focus_id; + BrowserAccessibility* obj = GetFromID(focus_id); + if (!obj) + return GetRoot(); - BrowserAccessibility* obj = GetFromAXNode(focus_); - DCHECK(obj); if (obj->HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { BrowserAccessibilityManager* child_manager = BrowserAccessibilityManager::FromID( obj->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); if (child_manager) - return child_manager->GetFocus(child_manager->GetRoot()); + return child_manager->GetFocusFromThisOrDescendantFrame(); } return obj; } -void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) { - if (focus_ != node) - focus_ = node; +void BrowserAccessibilityManager::SetFocus(const BrowserAccessibility& node) { + if (delegate_) + delegate_->AccessibilitySetFocus(node.GetId()); +} - if (notify && node && delegate_) - delegate_->AccessibilitySetFocus(node->id()); +void BrowserAccessibilityManager::SetFocusLocallyForTesting( + BrowserAccessibility* node) { + ui::AXTreeData data = GetTreeData(); + data.focus_id = node->GetId(); + tree_->UpdateData(data); } -void BrowserAccessibilityManager::SetFocus( - BrowserAccessibility* obj, bool notify) { - if (obj->node()) - SetFocus(obj->node(), notify); +// static +void BrowserAccessibilityManager::SetFocusChangeCallbackForTesting( + const base::Closure& callback) { + g_focus_change_callback_for_testing.Get() = callback; } void BrowserAccessibilityManager::DoDefaultAction( @@ -500,33 +600,35 @@ gfx::Rect BrowserAccessibilityManager::GetViewBounds() { return gfx::Rect(); } +// static BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder( - BrowserAccessibility* node) const { - if (!node) + const BrowserAccessibility* object) { + if (!object) return nullptr; - if (node->PlatformChildCount()) - return node->PlatformGetChild(0); + if (object->PlatformChildCount()) + return object->PlatformGetChild(0); - while (node) { - const auto sibling = node->GetNextSibling(); + while (object) { + BrowserAccessibility* sibling = object->GetNextSibling(); if (sibling) return sibling; - node = node->GetParent(); + object = object->GetParent(); } return nullptr; } +// static BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder( - BrowserAccessibility* node) const { - if (!node) + const BrowserAccessibility* object) { + if (!object) return nullptr; - const auto sibling = node->GetPreviousSibling(); + BrowserAccessibility* sibling = object->GetPreviousSibling(); if (!sibling) - return node->GetParent(); + return object->GetParent(); if (sibling->PlatformChildCount()) return sibling->PlatformDeepestLastChild(); @@ -534,36 +636,168 @@ BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder( return sibling; } +// static BrowserAccessibility* BrowserAccessibilityManager::PreviousTextOnlyObject( - BrowserAccessibility* node) const { - BrowserAccessibility* previous_node = PreviousInTreeOrder(node); - while (previous_node && !previous_node->IsTextOnlyObject()) - previous_node = PreviousInTreeOrder(previous_node); + const BrowserAccessibility* object) { + BrowserAccessibility* previous_object = PreviousInTreeOrder(object); + while (previous_object && !previous_object->IsTextOnlyObject()) + previous_object = PreviousInTreeOrder(previous_object); - return previous_node; + return previous_object; } +// static BrowserAccessibility* BrowserAccessibilityManager::NextTextOnlyObject( - BrowserAccessibility* node) const { - BrowserAccessibility* next_node = NextInTreeOrder(node); - while (next_node && !next_node->IsTextOnlyObject()) - next_node = NextInTreeOrder(next_node); + const BrowserAccessibility* object) { + BrowserAccessibility* next_object = NextInTreeOrder(object); + while (next_object && !next_object->IsTextOnlyObject()) + next_object = NextInTreeOrder(next_object); + + return next_object; +} + +// static +bool BrowserAccessibilityManager::FindIndicesInCommonParent( + const BrowserAccessibility& object1, + const BrowserAccessibility& object2, + BrowserAccessibility** common_parent, + int* child_index1, + int* child_index2) { + DCHECK(common_parent && child_index1 && child_index2); + auto ancestor1 = const_cast(&object1); + auto ancestor2 = const_cast(&object2); + do { + *child_index1 = ancestor1->GetIndexInParent(); + ancestor1 = ancestor1->GetParent(); + } while ( + ancestor1 && + // |BrowserAccessibility::IsAncestorOf| returns true if objects are equal. + (ancestor1 == ancestor2 || !ancestor2->IsDescendantOf(ancestor1))); + + if (!ancestor1) { + *common_parent = nullptr; + *child_index1 = -1; + *child_index2 = -1; + return false; + } + + do { + *child_index2 = ancestor2->GetIndexInParent(); + ancestor2 = ancestor2->GetParent(); + } while (ancestor1 != ancestor2); + + *common_parent = ancestor1; + return true; +} + +// static +base::string16 BrowserAccessibilityManager::GetTextForRange( + const BrowserAccessibility& start_object, + int start_offset, + const BrowserAccessibility& end_object, + int end_offset) { + DCHECK_GE(start_offset, 0); + DCHECK_GE(end_offset, 0); + + if (&start_object == &end_object && start_object.IsSimpleTextControl()) { + if (start_offset > end_offset) + std::swap(start_offset, end_offset); + + if (start_offset >= static_cast(start_object.GetValue().length()) || + end_offset > static_cast(start_object.GetValue().length())) { + return base::string16(); + } + + return start_object.GetValue().substr(start_offset, + end_offset - start_offset); + } + + int child_index1 = -1; + int child_index2 = -1; + if (&start_object != &end_object) { + BrowserAccessibility* common_parent; + if (!FindIndicesInCommonParent(start_object, end_object, &common_parent, + &child_index1, &child_index2)) { + return base::string16(); + } - return next_node; + DCHECK(common_parent); + DCHECK_GE(child_index1, 0); + DCHECK_GE(child_index2, 0); + // If the child indices are equal, one object is a descendant of the other. + DCHECK(child_index1 != child_index2 || + start_object.IsDescendantOf(&end_object) || + end_object.IsDescendantOf(&start_object)); + } + + const BrowserAccessibility* start_text_object = nullptr; + const BrowserAccessibility* end_text_object = nullptr; + if (child_index1 <= child_index2 || + end_object.IsDescendantOf(&start_object)) { + start_text_object = &start_object; + end_text_object = &end_object; + } else if (child_index1 > child_index2 || + start_object.IsDescendantOf(&end_object)) { + start_text_object = &end_object; + end_text_object = &start_object; + } + + if (!start_text_object->PlatformIsLeaf()) + start_text_object = start_text_object->PlatformDeepestFirstChild(); + if (!end_text_object->PlatformIsLeaf()) + end_text_object = end_text_object->PlatformDeepestLastChild(); + + if (!start_text_object->IsTextOnlyObject()) + start_text_object = NextTextOnlyObject(start_text_object); + if (!end_text_object->IsTextOnlyObject()) + end_text_object = PreviousTextOnlyObject(end_text_object); + + if (!start_text_object || !end_text_object) + return base::string16(); + + // Be a little permissive with the start and end offsets. + if (start_text_object == end_text_object) { + if (start_offset > end_offset) + std::swap(start_offset, end_offset); + + if (start_offset < + static_cast(start_text_object->GetText().length()) && + end_offset <= static_cast(end_text_object->GetText().length())) { + return start_text_object->GetText().substr(start_offset, + end_offset - start_offset); + } + return start_text_object->GetText(); + } + + base::string16 text; + if (start_offset < static_cast(start_text_object->GetText().length())) + text += start_text_object->GetText().substr(start_offset); + else + text += start_text_object->GetText(); + start_text_object = NextTextOnlyObject(start_text_object); + while (start_text_object && start_text_object != end_text_object) { + text += start_text_object->GetText(); + start_text_object = NextTextOnlyObject(start_text_object); + } + if (end_offset <= static_cast(end_text_object->GetText().length())) + text += end_text_object->GetText().substr(0, end_offset); + else + text += end_text_object->GetText(); + + return text; } +void BrowserAccessibilityManager::OnNodeDataWillChange( + ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) {} + void BrowserAccessibilityManager::OnTreeDataChanged(ui::AXTree* tree) { } void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) { DCHECK(node); - if (node == focus_ && tree_) { - if (node != tree_->root()) - SetFocus(tree_->root(), false); - else - focus_ = NULL; - } if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end()) return; GetFromAXNode(node)->Destroy(); @@ -604,26 +838,34 @@ void BrowserAccessibilityManager::OnAtomicUpdateFinished( ax_tree_id_changed = true; } - if (ax_tree_id_changed || root_changed) { - BrowserAccessibility* parent = GetParentNodeFromParentTree(); - if (parent) { - parent->OnDataChanged(); - parent->manager()->NotifyAccessibilityEvent( - ui::AX_EVENT_CHILDREN_CHANGED, parent); - } + // Whenever the tree ID or the root of this tree changes we may need to + // fire an event on our parent node in the parent tree to ensure that + // we're properly connected. + if (ax_tree_id_changed || root_changed) + connected_to_parent_tree_node_ = false; + + // When the root changes and this is the root manager, we may need to + // fire a new focus event. + if (root_changed && last_focused_manager_ == this) { + last_focused_node_ = nullptr; + last_focused_manager_ = nullptr; } } +BrowserAccessibilityManager* BrowserAccessibilityManager::GetRootManager() { + BrowserAccessibility* parent = GetParentNodeFromParentTree(); + if (!parent) + return this; + + return parent->manager()->GetRootManager(); +} + BrowserAccessibilityDelegate* BrowserAccessibilityManager::GetDelegateFromRootManager() { - if (!GetRoot()) - return nullptr; - int parent_tree_id = GetTreeData().parent_tree_id; - BrowserAccessibilityManager* parent_manager = - BrowserAccessibilityManager::FromID(parent_tree_id); - if (parent_manager) - return parent_manager->GetDelegateFromRootManager(); - return delegate(); + BrowserAccessibilityManager* root_manager = GetRootManager(); + if (root_manager) + return root_manager->delegate(); + return nullptr; } ui::AXTreeUpdate diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h index 01dcf37a083..6d5a42f720e 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h @@ -9,6 +9,7 @@ #include +#include "base/callback_forward.h" #include "base/containers/hash_tables.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -35,6 +36,8 @@ class BrowserAccessibilityManagerWin; class BrowserAccessibilityManagerAuraLinux; #endif +class SiteInstance; + // For testing. CONTENT_EXPORT ui::AXTreeUpdate MakeAXTreeUpdate( const ui::AXNodeData& node, @@ -79,6 +82,9 @@ class CONTENT_EXPORT BrowserAccessibilityDelegate { virtual gfx::Rect AccessibilityGetViewBounds() const = 0; virtual gfx::Point AccessibilityOriginInScreen( const gfx::Rect& bounds) const = 0; + virtual gfx::Rect AccessibilityTransformToRootCoordSpace( + const gfx::Rect& bounds) = 0; + virtual SiteInstance* AccessibilityGetSiteInstance() = 0; virtual void AccessibilityHitTest( const gfx::Point& point) = 0; virtual void AccessibilitySetAccessibilityFocus(int acc_obj_id) = 0; @@ -138,6 +144,18 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { virtual void NotifyAccessibilityEvent( ui::AXEvent event_type, BrowserAccessibility* node) { } + // Checks whether focus has changed since the last time it was checked, + // taking into account whether the window has focus and which frame within + // the frame tree has focus. If focus has changed, calls FireFocusEvent. + void FireFocusEventsIfNeeded(); + + // Return whether or not we are currently able to fire events. + virtual bool CanFireEvents(); + + // Fire a focus event. Virtual so that some platforms can customize it, + // like firing a focus event on the root first, on Windows. + virtual void FireFocusEvent(BrowserAccessibility* node); + // Return a pointer to the root of the tree, does not make a new reference. BrowserAccessibility* GetRoot(); @@ -172,11 +190,16 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { // occurred in the tab. void GotMouseDown(); - // Update the focused node to |node|, which may be null. - // If |notify| is true, send a message to the renderer to set focus - // to this node. - void SetFocus(ui::AXNode* node, bool notify); - void SetFocus(BrowserAccessibility* node, bool notify); + // Send a message to the renderer to set focus to this node. + void SetFocus(const BrowserAccessibility& node); + + // Pretend that the given node has focus, for testing only. Doesn't + // communicate with the renderer and doesn't fire any events. + void SetFocusLocallyForTesting(BrowserAccessibility* node); + + // For testing only, register a function to be called when focus changes + // in any BrowserAccessibilityManager. + static void SetFocusChangeCallbackForTesting(const base::Closure& callback); // Tell the renderer to do the default action for this node. void DoDefaultAction(const BrowserAccessibility& node); @@ -213,12 +236,12 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { void ActivateFindInPageResult(int request_id, int match_index); // Called when the renderer process has notified us of about tree changes. - void OnAccessibilityEvents( + virtual void OnAccessibilityEvents( const std::vector& details); // Called when the renderer process updates the location of accessibility - // objects. - virtual void OnLocationChanges( + // objects. Calls SendLocationChangeEvents(), which can be overridden. + void OnLocationChanges( const std::vector& params); // Called when a new find in page result is received. We hold on to this @@ -227,6 +250,11 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { int request_id, int match_index, int start_id, int start_offset, int end_id, int end_offset); + // Called in response to a hit test, when the object hit has a child frame + // (like an iframe element or browser plugin), and we need to do another + // hit test recursively. + void OnChildFrameHitTestResult(const gfx::Point& point, int hit_obj_id); + // This is called when the user has committed to a find in page query, // e.g. by pressing enter or tapping on the next / previous result buttons. // If a match has already been received for this request id, @@ -248,13 +276,16 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { ToBrowserAccessibilityManagerAuraLinux(); #endif - // Return the object that has focus, if it's a descendant of the - // given root (inclusive). Does not make a new reference. - virtual BrowserAccessibility* GetFocus(BrowserAccessibility* root); + // Return the object that has focus, starting at the top of the frame tree. + virtual BrowserAccessibility* GetFocus(); - // Return the descentant of the given root that has focus, or that object's - // active descendant if it has one. - BrowserAccessibility* GetActiveDescendantFocus(BrowserAccessibility* root); + // Return the object that has focus, only considering this frame and + // descendants. + BrowserAccessibility* GetFocusFromThisOrDescendantFrame(); + + // Given a focused node |focus|, returns a descendant of that node if it + // has an active descendant, otherwise returns |focus|. + BrowserAccessibility* GetActiveDescendantFocus(BrowserAccessibility* focus); // Returns true if native focus is anywhere in this WebContents or not. bool NativeViewHasFocus(); @@ -264,13 +295,41 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { virtual bool UseRootScrollOffsetsWhenComputingBounds(); // Walk the tree. - BrowserAccessibility* NextInTreeOrder(BrowserAccessibility* node) const; - BrowserAccessibility* PreviousInTreeOrder(BrowserAccessibility* node) const; - BrowserAccessibility* NextTextOnlyObject(BrowserAccessibility* node) const; - BrowserAccessibility* PreviousTextOnlyObject( - BrowserAccessibility* node) const; + static BrowserAccessibility* NextInTreeOrder( + const BrowserAccessibility* object); + static BrowserAccessibility* PreviousInTreeOrder( + const BrowserAccessibility* object); + static BrowserAccessibility* NextTextOnlyObject( + const BrowserAccessibility* object); + static BrowserAccessibility* PreviousTextOnlyObject( + const BrowserAccessibility* object); + + // If the two objects provided have a common ancestor returns both the + // common ancestor and the child indices of the two subtrees in which the + // objects are located. + // Returns false if a common ancestor cannot be found. + static bool FindIndicesInCommonParent(const BrowserAccessibility& object1, + const BrowserAccessibility& object2, + BrowserAccessibility** common_parent, + int* child_index1, + int* child_index2); + + // Returns all the text found between the given start and end locations. + // If start and end offsets are greater than the text's length, returns all + // the text until text length. + static base::string16 GetTextForRange( + const BrowserAccessibility& start_object, + int start_offset, + const BrowserAccessibility& end_object, + int end_offset); + + // Accessors. + AXTreeIDRegistry::AXTreeID ax_tree_id() const { return ax_tree_id_; } // AXTreeDelegate implementation. + void OnNodeDataWillChange(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; void OnTreeDataChanged(ui::AXTree* tree) override; void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; @@ -287,8 +346,11 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { } // If this BrowserAccessibilityManager is a child frame or guest frame, - // return the BrowserAccessibilityDelegate from the highest ancestor frame + // return the BrowserAccessibilityManager from the highest ancestor frame // in the frame tree. + BrowserAccessibilityManager* GetRootManager(); + + // Returns the BrowserAccessibilityDelegate from |GetRootManager|, above. BrowserAccessibilityDelegate* GetDelegateFromRootManager(); // Get a snapshot of the current tree as an AXTreeUpdate. @@ -304,6 +366,12 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { BrowserAccessibilityDelegate* delegate, BrowserAccessibilityFactory* factory); + // Send platform-specific notifications to each of these objects that + // their location has changed. This is called by OnLocationChanges + // after it's updated the internal data structure. + virtual void SendLocationChangeEvents( + const std::vector& params); + private: // The following states keep track of whether or not the // on-screen keyboard is allowed to be shown. @@ -337,9 +405,6 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { // The underlying tree of accessibility objects. scoped_ptr tree_; - // The node that currently has focus. - ui::AXNode* focus_; - // A mapping from a node id to its wrapper of type BrowserAccessibility. base::hash_map id_wrapper_map_; @@ -351,6 +416,21 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { BrowserAccessibilityFindInPageInfo find_in_page_info_; + // These are only used by the root BrowserAccessibilityManager of a + // frame tree. Stores the last focused node and last focused manager so + // that when focus might have changed we can figure out whether we need + // to fire a focus event. + // + // NOTE: these pointers are not cleared, so they should never be + // dereferenced, only used for comparison. + BrowserAccessibility* last_focused_node_; + BrowserAccessibilityManager* last_focused_manager_; + + // True if the root's parent is in another accessibility tree and that + // parent's child is the root. Ensures that the parent node is notified + // once when this subtree is first connected. + bool connected_to_parent_tree_node_; + // The global ID of this accessibility tree. AXTreeIDRegistry::AXTreeID ax_tree_id_; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc index 42c2042c5c4..abfdcf7b558 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.cc @@ -15,6 +15,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "content/browser/accessibility/browser_accessibility_android.h" +#include "content/browser/accessibility/one_shot_accessibility_tree_search.h" #include "content/common/accessibility_messages.h" #include "jni/BrowserAccessibilityManager_jni.h" #include "ui/accessibility/ax_text_utils.h" @@ -22,32 +23,99 @@ using base::android::AttachCurrentThread; using base::android::ScopedJavaLocalRef; +namespace content { + namespace { -enum AndroidHtmlElementType { - HTML_ELEMENT_TYPE_SECTION, - HTML_ELEMENT_TYPE_LIST, - HTML_ELEMENT_TYPE_CONTROL, - HTML_ELEMENT_TYPE_ANY -}; +using SearchKeyToPredicateMap = + base::hash_map; +base::LazyInstance g_search_key_to_predicate_map = + LAZY_INSTANCE_INITIALIZER; +base::LazyInstance g_all_search_keys = + LAZY_INSTANCE_INITIALIZER; + +bool SectionPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + switch (node->GetRole()) { + case ui::AX_ROLE_ARTICLE: + case ui::AX_ROLE_APPLICATION: + case ui::AX_ROLE_BANNER: + case ui::AX_ROLE_COMPLEMENTARY: + case ui::AX_ROLE_CONTENT_INFO: + case ui::AX_ROLE_HEADING: + case ui::AX_ROLE_MAIN: + case ui::AX_ROLE_NAVIGATION: + case ui::AX_ROLE_SEARCH: + case ui::AX_ROLE_REGION: + return true; + default: + return false; + } +} + +void AddToPredicateMap(const char* search_key_ascii, + AccessibilityMatchPredicate predicate) { + base::string16 search_key_utf16 = base::ASCIIToUTF16(search_key_ascii); + g_search_key_to_predicate_map.Get()[search_key_utf16] = predicate; + if (!g_all_search_keys.Get().empty()) + g_all_search_keys.Get() += base::ASCIIToUTF16(","); + g_all_search_keys.Get() += search_key_utf16; +} // These are special unofficial strings sent from TalkBack/BrailleBack // to jump to certain categories of web elements. -AndroidHtmlElementType HtmlElementTypeFromString(base::string16 element_type) { - if (element_type == base::ASCIIToUTF16("SECTION")) - return HTML_ELEMENT_TYPE_SECTION; - else if (element_type == base::ASCIIToUTF16("LIST")) - return HTML_ELEMENT_TYPE_LIST; - else if (element_type == base::ASCIIToUTF16("CONTROL")) - return HTML_ELEMENT_TYPE_CONTROL; - else - return HTML_ELEMENT_TYPE_ANY; +void InitSearchKeyToPredicateMapIfNeeded() { + if (!g_search_key_to_predicate_map.Get().empty()) + return; + + AddToPredicateMap("ARTICLE", AccessibilityArticlePredicate); + AddToPredicateMap("BUTTON", AccessibilityButtonPredicate); + AddToPredicateMap("CHECKBOX", AccessibilityCheckboxPredicate); + AddToPredicateMap("COMBOBOX", AccessibilityComboboxPredicate); + AddToPredicateMap("CONTROL", AccessibilityControlPredicate); + AddToPredicateMap("FOCUSABLE", AccessibilityFocusablePredicate); + AddToPredicateMap("FRAME", AccessibilityFramePredicate); + AddToPredicateMap("GRAPHIC", AccessibilityGraphicPredicate); + AddToPredicateMap("H1", AccessibilityH1Predicate); + AddToPredicateMap("H2", AccessibilityH2Predicate); + AddToPredicateMap("H3", AccessibilityH3Predicate); + AddToPredicateMap("H4", AccessibilityH4Predicate); + AddToPredicateMap("H5", AccessibilityH5Predicate); + AddToPredicateMap("H6", AccessibilityH6Predicate); + AddToPredicateMap("HEADING", AccessibilityHeadingPredicate); + AddToPredicateMap("LANDMARK", AccessibilityLandmarkPredicate); + AddToPredicateMap("LINK", AccessibilityLinkPredicate); + AddToPredicateMap("LIST", AccessibilityListPredicate); + AddToPredicateMap("LIST_ITEM", AccessibilityListItemPredicate); + AddToPredicateMap("MAIN", AccessibilityMainPredicate); + AddToPredicateMap("MEDIA", AccessibilityMediaPredicate); + AddToPredicateMap("RADIO", AccessibilityRadioButtonPredicate); + AddToPredicateMap("SECTION", SectionPredicate); + AddToPredicateMap("TABLE", AccessibilityTablePredicate); + AddToPredicateMap("TEXT_FIELD", AccessibilityTextfieldPredicate); + AddToPredicateMap("UNVISITED_LINK", AccessibilityUnvisitedLinkPredicate); + AddToPredicateMap("VISITED_LINK", AccessibilityVisitedLinkPredicate); +} + +AccessibilityMatchPredicate PredicateForSearchKey( + const base::string16& element_type) { + InitSearchKeyToPredicateMapIfNeeded(); + const auto& iter = g_search_key_to_predicate_map.Get().find(element_type); + if (iter != g_search_key_to_predicate_map.Get().end()) + return iter->second; + + // If we don't recognize the selector, return any element that's clickable. + // We mark all focusable nodes and leaf nodes as clickable because it's + // impossible to know whether a web node has a click handler or not, so + // to be safe we have to allow accessibility services to click on nearly + // anything that could possibly respond to a click. + return [](BrowserAccessibility* start, BrowserAccessibility* node) { + return static_cast(node)->IsClickable(); + }; } } // anonymous namespace -namespace content { - namespace aria_strings { const char kAriaLivePolite[] = "polite"; const char kAriaLiveAssertive[] = "assertive"; @@ -80,11 +148,12 @@ BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid( BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); + ScopedJavaLocalRef obj = GetJavaRefFromRootManager(); if (obj.is_null()) return; - Java_BrowserAccessibilityManager_onNativeObjectDestroyed(env, obj.obj()); + Java_BrowserAccessibilityManager_onNativeObjectDestroyed( + env, obj.obj(),reinterpret_cast(this)); } // static @@ -116,7 +185,7 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( ui::AXEvent event_type, BrowserAccessibility* node) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); + ScopedJavaLocalRef obj = GetJavaRefFromRootManager(); if (obj.is_null()) return; @@ -129,7 +198,8 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( if (event_type == ui::AX_EVENT_TREE_CHANGED) return; - // Layout changes are handled in OnLocationChanges. + // Layout changes are handled in OnLocationChanges and + // SendLocationChangeEvents. if (event_type == ui::AX_EVENT_LAYOUT_COMPLETE) return; @@ -142,28 +212,38 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( // the Android system that the accessibility hierarchy rooted at this // node has changed. Java_BrowserAccessibilityManager_handleContentChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); + + // Ignore load complete events on iframes. + if (event_type == ui::AX_EVENT_LOAD_COMPLETE && + node->manager() != GetRootManager()) { + return; + } switch (event_type) { case ui::AX_EVENT_LOAD_COMPLETE: Java_BrowserAccessibilityManager_handlePageLoaded( - env, obj.obj(), focus_->id()); + env, obj.obj(), GetFocus()->unique_id()); break; case ui::AX_EVENT_FOCUS: Java_BrowserAccessibilityManager_handleFocusChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); break; case ui::AX_EVENT_CHECKED_STATE_CHANGED: Java_BrowserAccessibilityManager_handleCheckStateChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); + break; + case ui::AX_EVENT_CLICKED: + Java_BrowserAccessibilityManager_handleClicked(env, obj.obj(), + node->unique_id()); break; case ui::AX_EVENT_SCROLL_POSITION_CHANGED: Java_BrowserAccessibilityManager_handleScrollPositionChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); break; case ui::AX_EVENT_SCROLLED_TO_ANCHOR: Java_BrowserAccessibilityManager_handleScrolledToAnchor( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); break; case ui::AX_EVENT_ALERT: // An alert is a special case of live region. Fall through to the @@ -179,16 +259,16 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( } case ui::AX_EVENT_TEXT_SELECTION_CHANGED: Java_BrowserAccessibilityManager_handleTextSelectionChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); break; case ui::AX_EVENT_TEXT_CHANGED: case ui::AX_EVENT_VALUE_CHANGED: - if (android_node->IsEditableText() && GetFocus(GetRoot()) == node) { + if (android_node->IsEditableText() && GetFocus() == node) { Java_BrowserAccessibilityManager_handleEditableTextChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); } else if (android_node->IsSlider()) { Java_BrowserAccessibilityManager_handleSliderChanged( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); } break; default: @@ -198,15 +278,15 @@ void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( } } -void BrowserAccessibilityManagerAndroid::OnLocationChanges( +void BrowserAccessibilityManagerAndroid::SendLocationChangeEvents( const std::vector& params) { // Android is not very efficient at handling notifications, and location // changes in particular are frequent and not time-critical. If a lot of // nodes changed location, just send a single notification after a short // delay (to batch them), rather than lots of individual notifications. if (params.size() > 3) { + ScopedJavaLocalRef obj = GetJavaRefFromRootManager(); JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); if (obj.is_null()) return; Java_BrowserAccessibilityManager_sendDelayedWindowContentChangedEvent( @@ -214,14 +294,22 @@ void BrowserAccessibilityManagerAndroid::OnLocationChanges( return; } - BrowserAccessibilityManager::OnLocationChanges(params); + BrowserAccessibilityManager::SendLocationChangeEvents(params); +} + +base::android::ScopedJavaLocalRef +BrowserAccessibilityManagerAndroid::GetSupportedHtmlElementTypes( + JNIEnv* env, + const JavaParamRef& obj) { + InitSearchKeyToPredicateMapIfNeeded(); + return base::android::ConvertUTF16ToJavaString(env, g_all_search_keys.Get()); } jint BrowserAccessibilityManagerAndroid::GetRootId( JNIEnv* env, const JavaParamRef& obj) { if (GetRoot()) - return static_cast(GetRoot()->GetId()); + return static_cast(GetRoot()->unique_id()); else return -1; } @@ -230,7 +318,7 @@ jboolean BrowserAccessibilityManagerAndroid::IsNodeValid( JNIEnv* env, const JavaParamRef& obj, jint id) { - return GetFromID(id) != NULL; + return GetFromUniqueID(id) != NULL; } void BrowserAccessibilityManagerAndroid::HitTest( @@ -246,8 +334,7 @@ jboolean BrowserAccessibilityManagerAndroid::IsEditableText( JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -258,8 +345,7 @@ jint BrowserAccessibilityManagerAndroid::GetEditableTextSelectionStart( JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -270,8 +356,7 @@ jint BrowserAccessibilityManagerAndroid::GetEditableTextSelectionEnd( JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -283,18 +368,17 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo( const JavaParamRef& obj, const JavaParamRef& info, jint id) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; if (node->GetParent()) { Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent( - env, obj, info, node->GetParent()->GetId()); + env, obj, info, node->GetParent()->unique_id()); } for (unsigned i = 0; i < node->PlatformChildCount(); ++i) { Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild( - env, obj, info, node->InternalGetChild(i)->GetId()); + env, obj, info, node->PlatformGetChild(i)->unique_id()); } Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes( env, obj, info, @@ -355,6 +439,12 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo( absolute_rect.width(), absolute_rect.height(), is_root); + Java_BrowserAccessibilityManager_setAccessibilityNodeInfoKitKatAttributes( + env, obj, info, + is_root, + base::android::ConvertUTF16ToJavaString( + env, node->GetRoleDescription()).obj()); + Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLollipopAttributes( env, obj, info, node->CanOpenPopup(), @@ -397,8 +487,7 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent( const JavaParamRef& event, jint id, jint event_type) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -500,7 +589,7 @@ jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent( void BrowserAccessibilityManagerAndroid::Click(JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (node) DoDefaultAction(*node); } @@ -508,22 +597,22 @@ void BrowserAccessibilityManagerAndroid::Click(JNIEnv* env, void BrowserAccessibilityManagerAndroid::Focus(JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (node) - SetFocus(node, true); + SetFocus(*node); } void BrowserAccessibilityManagerAndroid::Blur( JNIEnv* env, const JavaParamRef& obj) { - SetFocus(GetRoot(), true); + SetFocus(*GetRoot()); } void BrowserAccessibilityManagerAndroid::ScrollToMakeNodeVisible( JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (node) ScrollToMakeVisible(*node, gfx::Rect(node->GetLocation().size())); } @@ -533,7 +622,7 @@ void BrowserAccessibilityManagerAndroid::SetTextFieldValue( const JavaParamRef& obj, jint id, const JavaParamRef& value) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (node) { BrowserAccessibilityManager::SetValue( *node, base::android::ConvertJavaStringToUTF16(env, value)); @@ -546,7 +635,7 @@ void BrowserAccessibilityManagerAndroid::SetSelection( jint id, jint start, jint end) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (node) SetTextSelection(*node, start, end); } @@ -556,7 +645,7 @@ jboolean BrowserAccessibilityManagerAndroid::AdjustSlider( const JavaParamRef& obj, jint id, jboolean increment) { - BrowserAccessibility* node = GetFromID(id); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -589,7 +678,7 @@ jboolean BrowserAccessibilityManagerAndroid::AdjustSlider( void BrowserAccessibilityManagerAndroid::HandleHoverEvent( BrowserAccessibility* node) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); + ScopedJavaLocalRef obj = GetJavaRefFromRootManager(); if (obj.is_null()) return; @@ -606,7 +695,7 @@ void BrowserAccessibilityManagerAndroid::HandleHoverEvent( } Java_BrowserAccessibilityManager_handleHover( - env, obj.obj(), node->GetId()); + env, obj.obj(), node->unique_id()); } jint BrowserAccessibilityManagerAndroid::FindElementType( @@ -615,58 +704,36 @@ jint BrowserAccessibilityManagerAndroid::FindElementType( jint start_id, const JavaParamRef& element_type_str, jboolean forwards) { - BrowserAccessibility* node = GetFromID(start_id); - if (!node) + BrowserAccessibilityAndroid* start_node = GetFromUniqueID(start_id); + if (!start_node) return 0; - AndroidHtmlElementType element_type = HtmlElementTypeFromString( - base::android::ConvertJavaStringToUTF16(env, element_type_str)); + BrowserAccessibilityManager* root_manager = GetRootManager(); + if (!root_manager) + return 0; - node = forwards ? NextInTreeOrder(node) : PreviousInTreeOrder(node); - while (node) { - switch(element_type) { - case HTML_ELEMENT_TYPE_SECTION: - if (node->GetRole() == ui::AX_ROLE_ARTICLE || - node->GetRole() == ui::AX_ROLE_APPLICATION || - node->GetRole() == ui::AX_ROLE_BANNER || - node->GetRole() == ui::AX_ROLE_COMPLEMENTARY || - node->GetRole() == ui::AX_ROLE_CONTENT_INFO || - node->GetRole() == ui::AX_ROLE_HEADING || - node->GetRole() == ui::AX_ROLE_MAIN || - node->GetRole() == ui::AX_ROLE_NAVIGATION || - node->GetRole() == ui::AX_ROLE_SEARCH || - node->GetRole() == ui::AX_ROLE_REGION) { - return node->GetId(); - } - break; - case HTML_ELEMENT_TYPE_LIST: - if (node->GetRole() == ui::AX_ROLE_LIST || - node->GetRole() == ui::AX_ROLE_GRID || - node->GetRole() == ui::AX_ROLE_TABLE || - node->GetRole() == ui::AX_ROLE_TREE) { - return node->GetId(); - } - break; - case HTML_ELEMENT_TYPE_CONTROL: - if (static_cast(node)->IsFocusable()) - return node->GetId(); - break; - case HTML_ELEMENT_TYPE_ANY: - // In theory, the API says that an accessibility service could - // jump to an element by element name, like 'H1' or 'P'. This isn't - // currently used by any accessibility service, and we think it's - // better to keep them high-level like 'SECTION' or 'CONTROL', so we - // just fall back on linear navigation when we don't recognize the - // element type. - if (static_cast(node)->IsClickable()) - return node->GetId(); - break; - } + BrowserAccessibility* root = root_manager->GetRoot(); + if (!root) + return 0; - node = forwards ? NextInTreeOrder(node) : PreviousInTreeOrder(node); - } + AccessibilityMatchPredicate predicate = PredicateForSearchKey( + base::android::ConvertJavaStringToUTF16(env, element_type_str)); - return 0; + OneShotAccessibilityTreeSearch tree_search(root); + tree_search.SetStartNode(start_node); + tree_search.SetDirection( + forwards ? + OneShotAccessibilityTreeSearch::FORWARDS : + OneShotAccessibilityTreeSearch::BACKWARDS); + tree_search.SetResultLimit(1); + tree_search.SetImmediateDescendantsOnly(false); + tree_search.SetVisibleOnly(false); + tree_search.AddPredicate(predicate); + + if (tree_search.CountMatches() == 0) + return 0; + + return tree_search.GetMatchAtIndex(0)->unique_id(); } jboolean BrowserAccessibilityManagerAndroid::NextAtGranularity( @@ -676,8 +743,7 @@ jboolean BrowserAccessibilityManagerAndroid::NextAtGranularity( jboolean extend_selection, jint id, jint cursor_index) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -706,8 +772,7 @@ jboolean BrowserAccessibilityManagerAndroid::PreviousAtGranularity( jboolean extend_selection, jint id, jint cursor_index) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -827,8 +892,7 @@ bool BrowserAccessibilityManagerAndroid::IsSlider( JNIEnv* env, const JavaParamRef& obj, jint id) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -840,8 +904,7 @@ bool BrowserAccessibilityManagerAndroid::Scroll( const JavaParamRef& obj, jint id, int direction) { - BrowserAccessibilityAndroid* node = static_cast( - GetFromID(id)); + BrowserAccessibilityAndroid* node = GetFromUniqueID(id); if (!node) return false; @@ -852,9 +915,12 @@ void BrowserAccessibilityManagerAndroid::OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, const std::vector& changes) { + BrowserAccessibilityManager::OnAtomicUpdateFinished( + tree, root_changed, changes); + if (root_changed) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); + ScopedJavaLocalRef obj = GetJavaRefFromRootManager(); if (obj.is_null()) return; @@ -868,6 +934,24 @@ BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { return false; } +BrowserAccessibilityAndroid* +BrowserAccessibilityManagerAndroid::GetFromUniqueID(int32_t unique_id) { + return static_cast( + BrowserAccessibility::GetFromUniqueID(unique_id)); +} + +ScopedJavaLocalRef +BrowserAccessibilityManagerAndroid::GetJavaRefFromRootManager() { + BrowserAccessibilityManagerAndroid* root_manager = + static_cast( + GetRootManager()); + if (!root_manager) + return ScopedJavaLocalRef(); + + JNIEnv* env = AttachCurrentThread(); + return root_manager->java_ref().get(env); +} + bool RegisterBrowserAccessibilityManager(JNIEnv* env) { return RegisterNativesImpl(env); } diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h index 4bc42b54be5..d74699db24a 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_android.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_android.h @@ -77,7 +77,7 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid // Implementation of BrowserAccessibilityManager. void NotifyAccessibilityEvent(ui::AXEvent event_type, BrowserAccessibility* node) override; - void OnLocationChanges( + void SendLocationChangeEvents( const std::vector& params) override; @@ -85,6 +85,10 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid // Methods called from Java via JNI // -------------------------------------------------------------------------- + // Global methods. + base::android::ScopedJavaLocalRef GetSupportedHtmlElementTypes( + JNIEnv* env, const base::android::JavaParamRef& obj); + // Tree methods. jint GetRootId(JNIEnv* env, const base::android::JavaParamRef& obj); jboolean IsNodeValid(JNIEnv* env, @@ -214,6 +218,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid jint id, int direction); + JavaObjectWeakGlobalRef& java_ref() { return java_ref_; } + protected: // AXTreeDelegate overrides. void OnAtomicUpdateFinished( @@ -224,6 +230,10 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid bool UseRootScrollOffsetsWhenComputingBounds() override; private: + BrowserAccessibilityAndroid* GetFromUniqueID(int32_t unique_id); + + base::android::ScopedJavaLocalRef GetJavaRefFromRootManager(); + // This gives BrowserAccessibilityManager::Create access to the class // constructor. friend class BrowserAccessibilityManager; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h index 0cda76d2e93..715081920c4 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.h @@ -6,9 +6,16 @@ #define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_ #import +#include + +#include +#include #include "base/macros.h" +#include "base/strings/string16.h" +#import "content/browser/accessibility/browser_accessibility_cocoa.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/public/browser/ax_event_notification_details.h" namespace content { @@ -21,17 +28,26 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac BrowserAccessibilityDelegate* delegate, BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory()); + ~BrowserAccessibilityManagerMac() override; + static ui::AXTreeUpdate GetEmptyDocument(); - BrowserAccessibility* GetFocus(BrowserAccessibility* root) override; + BrowserAccessibility* GetFocus() override; // Implementation of BrowserAccessibilityManager. void NotifyAccessibilityEvent(ui::AXEvent event_type, BrowserAccessibility* node) override; + void OnAccessibilityEvents( + const std::vector& details) override; + NSView* parent_view() { return parent_view_; } private: + // AXTreeDelegate methods. + void OnNodeDataWillChange(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; void OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, @@ -40,12 +56,23 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac // Returns an autoreleased object. NSDictionary* GetUserInfoForSelectedTextChangedNotification(); + // Returns an autoreleased object. + NSDictionary* GetUserInfoForValueChangedNotification( + const BrowserAccessibilityCocoa* native_node, + const base::string16& deleted_text, + const base::string16& inserted_text) const; + // This gives BrowserAccessibilityManager::Create access to the class // constructor. friend class BrowserAccessibilityManager; NSView* parent_view_; + // Keeps track of any edits that have been made by the user during a tree + // update. Used by NSAccessibilityValueChangedNotification. + // Maps AXNode IDs to name or value attribute changes. + std::map text_edits_; + DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerMac); }; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm index 0937229b2aa..e43122e120b 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_mac.mm @@ -4,19 +4,19 @@ #include "content/browser/accessibility/browser_accessibility_manager_mac.h" -#include - #import "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" #include "base/logging.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" #import "content/browser/accessibility/browser_accessibility_cocoa.h" #import "content/browser/accessibility/browser_accessibility_mac.h" #include "content/common/accessibility_messages.h" - namespace { -// Declare accessibility constants and enums only present in WebKit. +// Declare undocumented accessibility constants and enums only present in +// WebKit. enum AXTextStateChangeType { AXTextStateChangeTypeUnknown, @@ -46,6 +46,17 @@ enum AXTextSelectionGranularity { AXTextSelectionGranularityAll }; +enum AXTextEditType { + AXTextEditTypeUnknown, + AXTextEditTypeDelete, + AXTextEditTypeInsert, + AXTextEditTypeTyping, + AXTextEditTypeDictation, + AXTextEditTypeCut, + AXTextEditTypePaste, + AXTextEditTypeAttributesChange +}; + NSString* const NSAccessibilityAutocorrectionOccurredNotification = @"AXAutocorrectionOccurred"; NSString* const NSAccessibilityLayoutCompleteNotification = @@ -58,6 +69,9 @@ NSString* const NSAccessibilityLiveRegionChangedNotification = @"AXLiveRegionChanged"; NSString* const NSAccessibilityMenuItemSelectedNotification = @"AXMenuItemSelected"; + +// Attributes used for NSAccessibilitySelectedTextChangedNotification and +// NSAccessibilityValueChangedNotification. NSString* const NSAccessibilityTextStateChangeTypeKey = @"AXTextStateChangeType"; NSString* const NSAccessibilityTextStateSyncKey = @"AXTextStateSync"; @@ -67,8 +81,14 @@ NSString* const NSAccessibilityTextSelectionGranularity = @"AXTextSelectionGranularity"; NSString* const NSAccessibilityTextSelectionChangedFocus = @"AXTextSelectionChangedFocus"; -NSString* const NSAccessibilityTextChangeElement = - @"AXAccessibilityTextChangeElement"; +NSString* const NSAccessibilitySelectedTextMarkerRangeAttribute = + @"AXSelectedTextMarkerRange"; +NSString* const NSAccessibilityTextChangeElement = @"AXTextChangeElement"; +NSString* const NSAccessibilityTextEditType = @"AXTextEditType"; +NSString* const NSAccessibilityTextChangeValue = @"AXTextChangeValue"; +NSString* const NSAccessibilityTextChangeValueLength = + @"AXTextChangeValueLength"; +NSString* const NSAccessibilityTextChangeValues = @"AXTextChangeValues"; } // namespace @@ -93,6 +113,8 @@ BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac( Initialize(initial_tree); } +BrowserAccessibilityManagerMac::~BrowserAccessibilityManagerMac() {} + // static ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() { @@ -106,16 +128,15 @@ ui::AXTreeUpdate return update; } -BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus( - BrowserAccessibility* root) { +BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() { // On Mac, list boxes should always get focus on the whole list, otherwise // information about the number of selected items will never be reported. - BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root); + BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(); if (node && node->GetRole() == ui::AX_ROLE_LIST_BOX) return node; // For other roles, follow the active descendant. - return GetActiveDescendantFocus(root); + return GetActiveDescendantFocus(node); } void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( @@ -125,8 +146,7 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( return; if (event_type == ui::AX_EVENT_FOCUS) { - BrowserAccessibility* active_descendant = GetActiveDescendantFocus( - GetRoot()); + BrowserAccessibility* active_descendant = GetActiveDescendantFocus(node); if (active_descendant) node = active_descendant; @@ -135,11 +155,11 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( node->GetParent() && node->GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { node = node->GetParent(); - SetFocus(node, false); + SetFocus(*node); } } - auto native_node = node->ToBrowserAccessibilityCocoa(); + auto native_node = ToBrowserAccessibilityCocoa(node); DCHECK(native_node); // Refer to |AXObjectCache::postPlatformNotification| in WebKit source code. @@ -182,6 +202,13 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( break; case ui::AX_EVENT_DOCUMENT_SELECTION_CHANGED: { mac_notification = NSAccessibilitySelectedTextChangedNotification; + // WebKit fires a notification both on the focused object and the root. + BrowserAccessibility* focus = GetFocus(); + if (!focus) + break; // Just fire a notification on the root. + NSAccessibilityPostNotification(ToBrowserAccessibilityCocoa(focus), + mac_notification); + if (base::mac::IsOSElCapitanOrLater()) { // |NSAccessibilityPostNotificationWithUserInfo| should be used on OS X // 10.11 or later to notify Voiceover about text selection changes. This @@ -189,15 +216,46 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( // appear to be needed by Voiceover before version 10.11. NSDictionary* user_info = GetUserInfoForSelectedTextChangedNotification(); + + BrowserAccessibility* root = GetRoot(); + if (!root) + return; + NSAccessibilityPostNotificationWithUserInfo( - native_node, mac_notification, user_info); + ToBrowserAccessibilityCocoa(focus), mac_notification, user_info); + NSAccessibilityPostNotificationWithUserInfo( + ToBrowserAccessibilityCocoa(root), mac_notification, user_info); return; } break; } case ui::AX_EVENT_CHECKED_STATE_CHANGED: + mac_notification = NSAccessibilityValueChangedNotification; + break; case ui::AX_EVENT_VALUE_CHANGED: mac_notification = NSAccessibilityValueChangedNotification; + if (base::mac::IsOSElCapitanOrLater() && text_edits_.size()) { + // It seems that we don't need to distinguish between deleted and + // inserted text for now. + base::string16 deleted_text; + base::string16 inserted_text; + int32_t id = node->GetId(); + const auto iterator = text_edits_.find(id); + if (iterator != text_edits_.end()) + inserted_text = iterator->second; + NSDictionary* user_info = GetUserInfoForValueChangedNotification( + native_node, deleted_text, inserted_text); + + BrowserAccessibility* root = GetRoot(); + if (!root) + return; + + NSAccessibilityPostNotificationWithUserInfo( + native_node, mac_notification, user_info); + NSAccessibilityPostNotificationWithUserInfo( + ToBrowserAccessibilityCocoa(root), mac_notification, user_info); + return; + } break; // TODO(nektar): Need to add an event for live region created. case ui::AX_EVENT_LIVE_REGION_CHANGED: @@ -244,6 +302,63 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( NSAccessibilityPostNotification(native_node, mac_notification); } +void BrowserAccessibilityManagerMac::OnAccessibilityEvents( + const std::vector& details) { + text_edits_.clear(); + // Call the base method last as it might delete the tree if it receives an + // invalid message. + BrowserAccessibilityManager::OnAccessibilityEvents(details); +} + +void BrowserAccessibilityManagerMac::OnNodeDataWillChange( + ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) { + BrowserAccessibilityManager::OnNodeDataWillChange(tree, old_node_data, + new_node_data); + + // Starting from OS X 10.11, if the user has edited some text we need to + // dispatch the actual text that changed on the value changed notification. + // We run this code on all OS X versions to get the highest test coverage. + base::string16 old_text, new_text; + ui::AXRole role = new_node_data.role; + if (role == ui::AX_ROLE_COMBO_BOX || role == ui::AX_ROLE_SEARCH_BOX || + role == ui::AX_ROLE_TEXT_FIELD) { + old_text = old_node_data.GetString16Attribute(ui::AX_ATTR_VALUE); + new_text = new_node_data.GetString16Attribute(ui::AX_ATTR_VALUE); + } else if (new_node_data.state & (1 << ui::AX_STATE_EDITABLE)) { + old_text = old_node_data.GetString16Attribute(ui::AX_ATTR_NAME); + new_text = new_node_data.GetString16Attribute(ui::AX_ATTR_NAME); + } + + if ((old_text.empty() && new_text.empty()) || + old_text.length() == new_text.length()) { + return; + } + + if (old_text.length() < new_text.length()) { + // Insertion. + size_t i = 0; + while (i < old_text.length() && i < new_text.length() && + old_text[i] == new_text[i]) { + ++i; + } + size_t length = (new_text.length() - i) - (old_text.length() - i); + base::string16 inserted_text = new_text.substr(i, length); + text_edits_[new_node_data.id] = inserted_text; + } else { + // Deletion. + size_t i = 0; + while (i < old_text.length() && i < new_text.length() && + old_text[i] == new_text[i]) { + ++i; + } + size_t length = (old_text.length() - i) - (new_text.length() - i); + base::string16 deleted_text = old_text.substr(i, length); + text_edits_[new_node_data.id] = deleted_text; + } +} + void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, @@ -281,35 +396,65 @@ void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished( } } -// Returns an autoreleased object. NSDictionary* BrowserAccessibilityManagerMac:: GetUserInfoForSelectedTextChangedNotification() { NSMutableDictionary* user_info = [[[NSMutableDictionary alloc] init] autorelease]; - [user_info setObject:[NSNumber numberWithBool:YES] - forKey:NSAccessibilityTextStateSyncKey]; - [user_info setObject:[NSNumber numberWithInt:AXTextStateChangeTypeUnknown] + [user_info setObject:@YES forKey:NSAccessibilityTextStateSyncKey]; + [user_info setObject:@(AXTextStateChangeTypeUnknown) forKey:NSAccessibilityTextStateChangeTypeKey]; - [user_info - setObject:[NSNumber numberWithInt:AXTextSelectionDirectionUnknown] - forKey:NSAccessibilityTextSelectionDirection]; - [user_info - setObject:[NSNumber numberWithInt:AXTextSelectionGranularityUnknown] - forKey:NSAccessibilityTextSelectionGranularity]; - [user_info setObject:[NSNumber numberWithBool:YES] - forKey:NSAccessibilityTextSelectionChangedFocus]; - // TODO(nektar): Set selected text marker range. + [user_info setObject:@(AXTextSelectionDirectionUnknown) + forKey:NSAccessibilityTextSelectionDirection]; + [user_info setObject:@(AXTextSelectionGranularityUnknown) + forKey:NSAccessibilityTextSelectionGranularity]; + [user_info setObject:@YES forKey:NSAccessibilityTextSelectionChangedFocus]; int32_t focus_id = GetTreeData().sel_focus_object_id; BrowserAccessibility* focus_object = GetFromID(focus_id); if (focus_object) { - auto native_focus_object = focus_object->ToBrowserAccessibilityCocoa(); - if (native_focus_object) + focus_object = focus_object->GetClosestPlatformObject(); + auto native_focus_object = ToBrowserAccessibilityCocoa(focus_object); + if (native_focus_object && [native_focus_object instanceActive]) { + [user_info setObject:[native_focus_object selectedTextMarkerRange] + forKey:NSAccessibilitySelectedTextMarkerRangeAttribute]; [user_info setObject:native_focus_object forKey:NSAccessibilityTextChangeElement]; + } } return user_info; } +NSDictionary* +BrowserAccessibilityManagerMac::GetUserInfoForValueChangedNotification( + const BrowserAccessibilityCocoa* native_node, + const base::string16& deleted_text, + const base::string16& inserted_text) const { + DCHECK(native_node); + if (deleted_text.empty() && inserted_text.empty()) + return nil; + + NSMutableArray* changes = [[[NSMutableArray alloc] init] autorelease]; + if (!deleted_text.empty()) { + [changes addObject:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeUnknown), + NSAccessibilityTextChangeValueLength : @(deleted_text.length()), + NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(deleted_text) + }]; + } + if (!inserted_text.empty()) { + [changes addObject:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeUnknown), + NSAccessibilityTextChangeValueLength : @(inserted_text.length()), + NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(inserted_text) + }]; + } + + return @{ + NSAccessibilityTextStateChangeTypeKey : @(AXTextStateChangeTypeEdit), + NSAccessibilityTextChangeValues : changes, + NSAccessibilityTextChangeElement : native_node + }; +} + } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc index c3a059652df..b13338f792e 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_unittest.cc @@ -88,6 +88,9 @@ class TestBrowserAccessibilityDelegate const gfx::Rect& bounds) const override { return gfx::Point(); } + gfx::Rect AccessibilityTransformToRootCoordSpace( + const gfx::Rect& bounds) override { return gfx::Rect(); } + SiteInstance* AccessibilityGetSiteInstance() override { return nullptr; } void AccessibilityHitTest(const gfx::Point& point) override {} void AccessibilitySetAccessibilityFocus(int acc_obj_id) override {} void AccessibilityFatalError() override { got_fatal_error_ = true; } @@ -967,17 +970,18 @@ TEST(BrowserAccessibilityManagerTest, TestNextPreviousInTreeOrder) { nullptr, new CountedBrowserAccessibilityFactory())); - auto root_accessible = manager->GetRoot(); + BrowserAccessibility* root_accessible = manager->GetRoot(); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(3U, root_accessible->PlatformChildCount()); - auto node2_accessible = root_accessible->PlatformGetChild(0); + BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, node2_accessible); - auto node3_accessible = root_accessible->PlatformGetChild(1); + BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1); ASSERT_NE(nullptr, node3_accessible); ASSERT_EQ(1U, node3_accessible->PlatformChildCount()); - auto node4_accessible = node3_accessible->PlatformGetChild(0); + BrowserAccessibility* node4_accessible = + node3_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, node4_accessible); - auto node5_accessible = root_accessible->PlatformGetChild(2); + BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2); ASSERT_NE(nullptr, node5_accessible); EXPECT_EQ(nullptr, manager->NextInTreeOrder(nullptr)); @@ -1042,26 +1046,30 @@ TEST(BrowserAccessibilityManagerTest, TestNextPreviousTextOnlyObject) { nullptr, new CountedBrowserAccessibilityFactory())); - auto root_accessible = manager->GetRoot(); + BrowserAccessibility* root_accessible = manager->GetRoot(); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(4U, root_accessible->PlatformChildCount()); - auto node2_accessible = root_accessible->PlatformGetChild(0); + BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, node2_accessible); - auto text1_accessible = root_accessible->PlatformGetChild(1); + BrowserAccessibility* text1_accessible = root_accessible->PlatformGetChild(1); ASSERT_NE(nullptr, text1_accessible); - auto node3_accessible = root_accessible->PlatformGetChild(2); + BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(2); ASSERT_NE(nullptr, node3_accessible); ASSERT_EQ(3U, node3_accessible->PlatformChildCount()); - auto text2_accessible = node3_accessible->PlatformGetChild(0); + BrowserAccessibility* text2_accessible = + node3_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, text2_accessible); - auto node4_accessible = node3_accessible->PlatformGetChild(1); + BrowserAccessibility* node4_accessible = + node3_accessible->PlatformGetChild(1); ASSERT_NE(nullptr, node4_accessible); - auto text3_accessible = node3_accessible->PlatformGetChild(2); + BrowserAccessibility* text3_accessible = + node3_accessible->PlatformGetChild(2); ASSERT_NE(nullptr, text3_accessible); - auto node5_accessible = root_accessible->PlatformGetChild(3); + BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(3); ASSERT_NE(nullptr, node5_accessible); ASSERT_EQ(1U, node5_accessible->PlatformChildCount()); - auto text4_accessible = node5_accessible->PlatformGetChild(0); + BrowserAccessibility* text4_accessible = + node5_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, text4_accessible); EXPECT_EQ(nullptr, manager->NextTextOnlyObject(nullptr)); @@ -1092,6 +1100,295 @@ TEST(BrowserAccessibilityManagerTest, TestNextPreviousTextOnlyObject) { EXPECT_EQ(nullptr, manager->PreviousTextOnlyObject(root_accessible)); } +TEST(BrowserAccessibilityManagerTest, TestFindIndicesInCommonParent) { + ui::AXNodeData root; + root.id = 1; + root.role = ui::AX_ROLE_ROOT_WEB_AREA; + + ui::AXNodeData div; + div.id = 2; + div.role = ui::AX_ROLE_DIV; + root.child_ids.push_back(div.id); + + ui::AXNodeData button; + button.id = 3; + button.role = ui::AX_ROLE_BUTTON; + div.child_ids.push_back(button.id); + + ui::AXNodeData button_text; + button_text.id = 4; + button_text.role = ui::AX_ROLE_STATIC_TEXT; + button_text.SetName("Button"); + button.child_ids.push_back(button_text.id); + + ui::AXNodeData line_break; + line_break.id = 5; + line_break.role = ui::AX_ROLE_LINE_BREAK; + line_break.SetName("\n"); + div.child_ids.push_back(line_break.id); + + ui::AXNodeData paragraph; + paragraph.id = 6; + paragraph.role = ui::AX_ROLE_PARAGRAPH; + root.child_ids.push_back(paragraph.id); + + ui::AXNodeData paragraph_text; + paragraph_text.id = 7; + paragraph_text.role = ui::AX_ROLE_STATIC_TEXT; + paragraph.child_ids.push_back(paragraph_text.id); + + ui::AXNodeData paragraph_line1; + paragraph_line1.id = 8; + paragraph_line1.role = ui::AX_ROLE_INLINE_TEXT_BOX; + paragraph_line1.SetName("Hello "); + paragraph_text.child_ids.push_back(paragraph_line1.id); + + ui::AXNodeData paragraph_line2; + paragraph_line2.id = 9; + paragraph_line2.role = ui::AX_ROLE_INLINE_TEXT_BOX; + paragraph_line2.SetName("world."); + paragraph_text.child_ids.push_back(paragraph_line2.id); + + scoped_ptr manager( + BrowserAccessibilityManager::Create( + MakeAXTreeUpdate(root, div, button, button_text, line_break, + paragraph, paragraph_text, paragraph_line1, + paragraph_line2), + nullptr, new CountedBrowserAccessibilityFactory())); + + BrowserAccessibility* root_accessible = manager->GetRoot(); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(2U, root_accessible->PlatformChildCount()); + BrowserAccessibility* div_accessible = root_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, div_accessible); + ASSERT_EQ(2U, div_accessible->PlatformChildCount()); + BrowserAccessibility* button_accessible = div_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, button_accessible); + BrowserAccessibility* button_text_accessible = + button_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, button_text_accessible); + BrowserAccessibility* line_break_accessible = + div_accessible->PlatformGetChild(1); + ASSERT_NE(nullptr, line_break_accessible); + BrowserAccessibility* paragraph_accessible = + root_accessible->PlatformGetChild(1); + ASSERT_NE(nullptr, paragraph_accessible); + BrowserAccessibility* paragraph_text_accessible = + paragraph_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, paragraph_text_accessible); + ASSERT_EQ(2U, paragraph_text_accessible->InternalChildCount()); + BrowserAccessibility* paragraph_line1_accessible = + paragraph_text_accessible->InternalGetChild(0); + ASSERT_NE(nullptr, paragraph_line1_accessible); + BrowserAccessibility* paragraph_line2_accessible = + paragraph_text_accessible->InternalGetChild(1); + ASSERT_NE(nullptr, paragraph_line2_accessible); + + BrowserAccessibility* common_parent = nullptr; + int child_index1, child_index2; + EXPECT_FALSE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *root_accessible, *root_accessible, &common_parent, &child_index1, + &child_index2)); + EXPECT_EQ(nullptr, common_parent); + EXPECT_EQ(-1, child_index1); + EXPECT_EQ(-1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *div_accessible, *paragraph_accessible, &common_parent, &child_index1, + &child_index2)); + EXPECT_EQ(root_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *div_accessible, *paragraph_line1_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(root_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *line_break_accessible, *paragraph_text_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(root_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *button_text_accessible, *line_break_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(div_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *paragraph_accessible, *paragraph_line2_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(root_accessible, common_parent); + EXPECT_EQ(1, child_index1); + EXPECT_EQ(1, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *paragraph_text_accessible, *paragraph_line1_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(paragraph_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(0, child_index2); + + EXPECT_TRUE(BrowserAccessibilityManager::FindIndicesInCommonParent( + *paragraph_line1_accessible, *paragraph_line2_accessible, &common_parent, + &child_index1, &child_index2)); + EXPECT_EQ(paragraph_text_accessible, common_parent); + EXPECT_EQ(0, child_index1); + EXPECT_EQ(1, child_index2); +} + +TEST(BrowserAccessibilityManagerTest, TestGetTextForRange) { + ui::AXNodeData root; + root.id = 1; + root.role = ui::AX_ROLE_ROOT_WEB_AREA; + + ui::AXNodeData div; + div.id = 2; + div.role = ui::AX_ROLE_DIV; + root.child_ids.push_back(div.id); + + ui::AXNodeData button; + button.id = 3; + button.role = ui::AX_ROLE_BUTTON; + div.child_ids.push_back(button.id); + + ui::AXNodeData button_text; + button_text.id = 4; + button_text.role = ui::AX_ROLE_STATIC_TEXT; + button_text.SetName("Button"); + button.child_ids.push_back(button_text.id); + + ui::AXNodeData line_break; + line_break.id = 5; + line_break.role = ui::AX_ROLE_LINE_BREAK; + line_break.SetName("\n"); + div.child_ids.push_back(line_break.id); + + ui::AXNodeData paragraph; + paragraph.id = 6; + paragraph.role = ui::AX_ROLE_PARAGRAPH; + root.child_ids.push_back(paragraph.id); + + ui::AXNodeData paragraph_text; + paragraph_text.id = 7; + paragraph_text.role = ui::AX_ROLE_STATIC_TEXT; + paragraph_text.SetName("Hello world."); + paragraph.child_ids.push_back(paragraph_text.id); + + ui::AXNodeData paragraph_line1; + paragraph_line1.id = 8; + paragraph_line1.role = ui::AX_ROLE_INLINE_TEXT_BOX; + paragraph_line1.SetName("Hello "); + paragraph_text.child_ids.push_back(paragraph_line1.id); + + ui::AXNodeData paragraph_line2; + paragraph_line2.id = 9; + paragraph_line2.role = ui::AX_ROLE_INLINE_TEXT_BOX; + paragraph_line2.SetName("world."); + paragraph_text.child_ids.push_back(paragraph_line2.id); + + scoped_ptr manager( + BrowserAccessibilityManager::Create( + MakeAXTreeUpdate(root, div, button, button_text, line_break, + paragraph, paragraph_text, paragraph_line1, + paragraph_line2), + nullptr, new CountedBrowserAccessibilityFactory())); + + BrowserAccessibility* root_accessible = manager->GetRoot(); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(2U, root_accessible->PlatformChildCount()); + BrowserAccessibility* div_accessible = root_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, div_accessible); + ASSERT_EQ(2U, div_accessible->PlatformChildCount()); + BrowserAccessibility* button_accessible = div_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, button_accessible); + BrowserAccessibility* button_text_accessible = + button_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, button_text_accessible); + BrowserAccessibility* line_break_accessible = + div_accessible->PlatformGetChild(1); + ASSERT_NE(nullptr, line_break_accessible); + BrowserAccessibility* paragraph_accessible = + root_accessible->PlatformGetChild(1); + ASSERT_NE(nullptr, paragraph_accessible); + BrowserAccessibility* paragraph_text_accessible = + paragraph_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, paragraph_text_accessible); + ASSERT_EQ(2U, paragraph_text_accessible->InternalChildCount()); + BrowserAccessibility* paragraph_line1_accessible = + paragraph_text_accessible->InternalGetChild(0); + ASSERT_NE(nullptr, paragraph_line1_accessible); + BrowserAccessibility* paragraph_line2_accessible = + paragraph_text_accessible->InternalGetChild(1); + ASSERT_NE(nullptr, paragraph_line2_accessible); + + EXPECT_EQ(base::ASCIIToUTF16("Button\nHello world."), + BrowserAccessibilityManager::GetTextForRange(*root_accessible, 0, + *root_accessible, 19)); + EXPECT_EQ(base::ASCIIToUTF16("ton\nHello world."), + BrowserAccessibilityManager::GetTextForRange(*root_accessible, 3, + *root_accessible, 19)); + EXPECT_EQ(base::ASCIIToUTF16("Button\nHello world."), + BrowserAccessibilityManager::GetTextForRange( + *div_accessible, 0, *paragraph_accessible, 12)); + EXPECT_EQ(base::ASCIIToUTF16("ton\nHello world."), + BrowserAccessibilityManager::GetTextForRange( + *div_accessible, 3, *paragraph_accessible, 12)); + + EXPECT_EQ(base::ASCIIToUTF16("Button\n"), + BrowserAccessibilityManager::GetTextForRange(*div_accessible, 0, + *div_accessible, 1)); + EXPECT_EQ(base::ASCIIToUTF16("Button\n"), + BrowserAccessibilityManager::GetTextForRange( + *button_accessible, 0, *line_break_accessible, 1)); + + EXPECT_EQ(base::ASCIIToUTF16("Hello world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_accessible, 0, *paragraph_accessible, 12)); + EXPECT_EQ(base::ASCIIToUTF16("Hello wor"), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_accessible, 0, *paragraph_accessible, 9)); + EXPECT_EQ(base::ASCIIToUTF16("Hello world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_text_accessible, 0, *paragraph_text_accessible, 12)); + EXPECT_EQ(base::ASCIIToUTF16(" world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_text_accessible, 5, *paragraph_text_accessible, 12)); + EXPECT_EQ(base::ASCIIToUTF16("Hello world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_accessible, 0, *paragraph_text_accessible, 12)); + EXPECT_EQ( + base::ASCIIToUTF16("Hello "), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line1_accessible, 0, *paragraph_line1_accessible, 6)); + EXPECT_EQ( + base::ASCIIToUTF16("Hello"), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line1_accessible, 0, *paragraph_line1_accessible, 5)); + EXPECT_EQ( + base::ASCIIToUTF16("ello "), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line1_accessible, 1, *paragraph_line1_accessible, 6)); + EXPECT_EQ( + base::ASCIIToUTF16("world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line2_accessible, 0, *paragraph_line2_accessible, 6)); + EXPECT_EQ( + base::ASCIIToUTF16("orld"), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line2_accessible, 1, *paragraph_line2_accessible, 5)); + EXPECT_EQ( + base::ASCIIToUTF16("Hello world."), + BrowserAccessibilityManager::GetTextForRange( + *paragraph_line1_accessible, 0, *paragraph_line2_accessible, 6)); +} + TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) { // Create a really simple tree with one root node and one focused child. ui::AXNodeData root; @@ -1102,27 +1399,18 @@ TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) { ui::AXNodeData node2; node2.id = 2; - node2.state = 1 << ui::AX_STATE_FOCUSED; + ui::AXTreeUpdate initial_state = MakeAXTreeUpdate(root, node2); + initial_state.has_tree_data = true; + initial_state.tree_data.focus_id = 2; scoped_ptr manager( BrowserAccessibilityManager::Create( - MakeAXTreeUpdate(root, node2), + initial_state, nullptr, new CountedBrowserAccessibilityFactory())); ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId()); - - // Send the focus event for node 2. - std::vector events; - events.push_back(AXEventNotificationDetails()); - events[0].update = MakeAXTreeUpdate(node2); - events[0].id = 2; - events[0].event_type = ui::AX_EVENT_FOCUS; - manager->OnAccessibilityEvents(events); - - ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(2, manager->GetFocus()->GetId()); // Now replace the tree with a new tree consisting of a single root. ui::AXNodeData root2; @@ -1140,7 +1428,7 @@ TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) { // Make sure that the focused node was updated to the new root and // that this doesn't crash. ASSERT_EQ(3, manager->GetRoot()->GetId()); - ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(3, manager->GetFocus()->GetId()); } TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { @@ -1155,7 +1443,6 @@ TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { ui::AXNodeData node2; node2.id = 2; - node2.state = 1 << ui::AX_STATE_FOCUSED; ui::AXNodeData node3; node3.id = 3; @@ -1165,25 +1452,17 @@ TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { node4.id = 4; node4.state = 0; + ui::AXTreeUpdate initial_state = MakeAXTreeUpdate(root, node2, node3, node4); + initial_state.has_tree_data = true; + initial_state.tree_data.focus_id = 2; scoped_ptr manager( BrowserAccessibilityManager::Create( - MakeAXTreeUpdate(root, node2, node3, node4), + initial_state, nullptr, new CountedBrowserAccessibilityFactory())); ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId()); - - // Send the focus event for node 2. - std::vector events; - events.push_back(AXEventNotificationDetails()); - events[0].update = MakeAXTreeUpdate(node2); - events[0].id = 2; - events[0].event_type = ui::AX_EVENT_FOCUS; - manager->OnAccessibilityEvents(events); - - ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(2, manager->GetFocus()->GetId()); // Now replace the tree with a new tree consisting of a single root. ui::AXNodeData root2; @@ -1203,7 +1482,7 @@ TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { // Make sure that the focused node was updated to the new root and // that this doesn't crash. ASSERT_EQ(3, manager->GetRoot()->GetId()); - ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(3, manager->GetFocus()->GetId()); } } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc index 4f963ebcb02..0595ea7d2fe 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.cc @@ -20,10 +20,6 @@ namespace content { -// Map from unique_id_win to BrowserAccessibility -using UniqueIDWinMap = base::hash_map; -base::LazyInstance g_unique_id_map = LAZY_INSTANCE_INITIALIZER; - // static BrowserAccessibilityManager* BrowserAccessibilityManager::Create( const ui::AXTreeUpdate& initial_tree, @@ -42,11 +38,10 @@ BrowserAccessibilityManagerWin::BrowserAccessibilityManagerWin( BrowserAccessibilityDelegate* delegate, BrowserAccessibilityFactory* factory) : BrowserAccessibilityManager(delegate, factory), - tracked_scroll_object_(NULL), - focus_event_on_root_needed_(false), - inside_on_window_focused_(false) { + tracked_scroll_object_(NULL) { ui::win::CreateATLModuleIfNeeded(); Initialize(initial_tree); + ui::GetIAccessible2UsageObserverList().AddObserver(this); } BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { @@ -59,6 +54,7 @@ BrowserAccessibilityManagerWin::~BrowserAccessibilityManagerWin() { tracked_scroll_object_->Release(); tracked_scroll_object_ = NULL; } + ui::GetIAccessible2UsageObserverList().RemoveObserver(this); } // static @@ -93,7 +89,8 @@ IAccessible* BrowserAccessibilityManagerWin::GetParentIAccessible() { void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent( DWORD event, BrowserAccessibility* node) { - BrowserAccessibilityDelegate* delegate = GetDelegateFromRootManager(); + BrowserAccessibilityDelegate* delegate = + node->manager()->GetDelegateFromRootManager(); if (!delegate) { // This line and other LOG(WARNING) lines are temporary, to debug // flaky failures in DumpAccessibilityEvent* tests. @@ -118,65 +115,19 @@ void BrowserAccessibilityManagerWin::MaybeCallNotifyWinEvent( // It doesn't make sense to fire a REORDER event on a leaf node; that // happens when the node has internal children line inline text boxes. - if (event == EVENT_OBJECT_REORDER && node->PlatformIsLeaf()) - return; - - // Don't fire focus, or load complete notifications if the - // window isn't focused, because that can confuse screen readers into - // entering their "browse" mode. - if ((event == EVENT_OBJECT_FOCUS || - event == IA2_EVENT_DOCUMENT_LOAD_COMPLETE) && - !NativeViewHasFocus()) { + if (event == EVENT_OBJECT_REORDER && node->PlatformChildCount() == 0) return; - } - // NVDA gets confused if we focus the main document element when it hasn't - // finished loading and it has no children at all, so suppress that event. - if (event == EVENT_OBJECT_FOCUS && - node == GetRoot() && - node->PlatformChildCount() == 0 && - !node->HasState(ui::AX_STATE_BUSY) && - !node->manager()->GetTreeData().loaded) { - return; - } - - // If a focus event is needed on the root, fire that first before - // this event. - if (event == EVENT_OBJECT_FOCUS && node == GetRoot()) - focus_event_on_root_needed_ = false; - else if (focus_event_on_root_needed_) - OnWindowFocused(); - - LONG child_id = node->ToBrowserAccessibilityWin()->unique_id_win(); + // Pass the negation of this node's unique id in the |child_id| + // argument to NotifyWinEvent; the AT client will then call get_accChild + // on the HWND's accessibility object and pass it that same id, which + // we can use to retrieve the IAccessible for this node. + LONG child_id = -node->unique_id(); ::NotifyWinEvent(event, hwnd, OBJID_CLIENT, child_id); } -void BrowserAccessibilityManagerWin::OnWindowFocused() { - // Make sure we don't call this recursively. - if (inside_on_window_focused_) - return; - inside_on_window_focused_ = true; - - // This is called either when this web frame gets focused, or when - // the root of the accessibility tree changes. In both cases, we need - // to fire a focus event on the root and then on the focused element - // within the page, if different. - - // Set this flag so that we'll keep trying to fire these focus events - // if they're not successful this time. - focus_event_on_root_needed_ = true; - - if (!NativeViewHasFocus()) { - inside_on_window_focused_ = false; - return; - } - - // Try to fire a focus event on the root first and then the focused node. - // This will clear focus_event_on_root_needed_ if successful. - if (focus_ != tree_->root() && GetRoot()) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); - BrowserAccessibilityManager::OnWindowFocused(); - inside_on_window_focused_ = false; +void BrowserAccessibilityManagerWin::OnIAccessible2Used() { + BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected(); } void BrowserAccessibilityManagerWin::UserIsReloading() { @@ -213,23 +164,6 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( return; } - // NVDA gets confused if we focus the main document element when it hasn't - // finished loading and it has no children at all, so suppress that event. - if (event_type == ui::AX_EVENT_FOCUS && - node == GetRoot() && - node->PlatformChildCount() == 0 && - !node->HasState(ui::AX_STATE_BUSY) && - !node->manager()->GetTreeData().loaded) { - return; - } - - // If a focus event is needed on the root, fire that first before - // this event. - if (event_type == ui::AX_EVENT_FOCUS && node == GetRoot()) - focus_event_on_root_needed_ = false; - else if (focus_event_on_root_needed_) - OnWindowFocused(); - LONG event_id = EVENT_MIN; switch (event_type) { case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED: @@ -287,13 +221,9 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( if (!node) return; - if (event_id != EVENT_MIN) { - // Pass the node's unique id in the |child_id| argument to NotifyWinEvent; - // the AT client will then call get_accChild on the HWND's accessibility - // object and pass it that same id, which we can use to retrieve the - // IAccessible for this node. + if (event_id != EVENT_MIN) MaybeCallNotifyWinEvent(event_id, node); - } + // If this is a layout complete notification (sent when a container scrolls) // and there is a descendant tracked object, send a notification on it. @@ -308,6 +238,26 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( } } +bool BrowserAccessibilityManagerWin::CanFireEvents() { + BrowserAccessibilityDelegate* root_delegate = GetDelegateFromRootManager(); + if (!root_delegate) + return false; + HWND hwnd = root_delegate->AccessibilityGetAcceleratedWidget(); + return hwnd != nullptr; +} + +void BrowserAccessibilityManagerWin::FireFocusEvent( + BrowserAccessibility* node) { + // On Windows, we always fire a FOCUS event on the root of a frame before + // firing a focus event within that frame. + if (node->manager() != last_focused_manager_ && + node != node->manager()->GetRoot()) { + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, node->manager()->GetRoot()); + } + + BrowserAccessibilityManager::FireFocusEvent(node); +} + void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) { DCHECK(node); @@ -317,8 +267,6 @@ void BrowserAccessibilityManagerWin::OnNodeCreated(ui::AXTree* tree, return; if (!obj->IsNative()) return; - LONG unique_id_win = obj->ToBrowserAccessibilityWin()->unique_id_win(); - g_unique_id_map.Get()[unique_id_win] = obj; } void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXTree* tree, @@ -326,8 +274,6 @@ void BrowserAccessibilityManagerWin::OnNodeWillBeDeleted(ui::AXTree* tree, DCHECK(node); BrowserAccessibility* obj = GetFromAXNode(node); if (obj && obj->IsNative()) { - g_unique_id_map.Get().erase( - obj->ToBrowserAccessibilityWin()->unique_id_win()); if (obj == tracked_scroll_object_) { tracked_scroll_object_->Release(); tracked_scroll_object_ = NULL; @@ -346,12 +292,6 @@ void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( BrowserAccessibilityManager::OnAtomicUpdateFinished( tree, root_changed, changes); - if (root_changed) { - // In order to make screen readers aware of the new accessibility root, - // we need to fire a focus event on it. - OnWindowFocused(); - } - // Do a sequence of Windows-specific updates on each node. Each one is // done in a single pass that must complete before the next step starts. // The first step moves win_attributes_ to old_win_attributes_ and then @@ -361,7 +301,7 @@ void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( DCHECK(changed_node); BrowserAccessibility* obj = GetFromAXNode(changed_node); if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf()) - obj->ToBrowserAccessibilityWin()->UpdateStep1ComputeWinAttributes(); + ToBrowserAccessibilityWin(obj)->UpdateStep1ComputeWinAttributes(); } // The next step updates the hypertext of each node, which is a @@ -372,7 +312,7 @@ void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( DCHECK(changed_node); BrowserAccessibility* obj = GetFromAXNode(changed_node); if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf()) - obj->ToBrowserAccessibilityWin()->UpdateStep2ComputeHypertext(); + ToBrowserAccessibilityWin(obj)->UpdateStep2ComputeHypertext(); } // The third step fires events on nodes based on what's changed - like @@ -388,7 +328,7 @@ void BrowserAccessibilityManagerWin::OnAtomicUpdateFinished( DCHECK(changed_node); BrowserAccessibility* obj = GetFromAXNode(changed_node); if (obj && obj->IsNative() && !obj->PlatformIsChildOfLeaf()) { - obj->ToBrowserAccessibilityWin()->UpdateStep3FireEvents( + ToBrowserAccessibilityWin(obj)->UpdateStep3FireEvents( changes[i].type == AXTreeDelegate::SUBTREE_CREATED); } } @@ -402,13 +342,4 @@ void BrowserAccessibilityManagerWin::TrackScrollingObject( tracked_scroll_object_->AddRef(); } -BrowserAccessibilityWin* BrowserAccessibilityManagerWin::GetFromUniqueIdWin( - LONG unique_id_win) { - auto iter = g_unique_id_map.Get().find(unique_id_win); - if (iter == g_unique_id_map.Get().end()) - return nullptr; - - return iter->second->ToBrowserAccessibilityWin(); -} - } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h index b9da9a12c6a..04b8aa241bb 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager_win.h @@ -11,13 +11,15 @@ #include "base/memory/scoped_ptr.h" #include "base/win/scoped_comptr.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "ui/accessibility/platform/ax_platform_node_win.h" namespace content { class BrowserAccessibilityWin; // Manages a tree of BrowserAccessibilityWin objects. class CONTENT_EXPORT BrowserAccessibilityManagerWin - : public BrowserAccessibilityManager { + : public BrowserAccessibilityManager, + public ui::IAccessible2UsageObserver { public: BrowserAccessibilityManagerWin( const ui::AXTreeUpdate& initial_tree, @@ -37,26 +39,26 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin // Calls NotifyWinEvent if the parent window's IAccessible pointer is known. void MaybeCallNotifyWinEvent(DWORD event, BrowserAccessibility* node); + // IAccessible2UsageObserver + void OnIAccessible2Used() override; + // BrowserAccessibilityManager methods - void OnWindowFocused() override; void UserIsReloading() override; void NotifyAccessibilityEvent( ui::AXEvent event_type, BrowserAccessibility* node) override; + bool CanFireEvents() override; + void FireFocusEvent(BrowserAccessibility* node) override; // Track this object and post a VISIBLE_DATA_CHANGED notification when // its container scrolls. // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. void TrackScrollingObject(BrowserAccessibilityWin* node); - // Return a pointer to the object corresponding to the given windows-specific - // unique id, does not make a new reference. - BrowserAccessibilityWin* GetFromUniqueIdWin(LONG unique_id_win); - // Called when |accessible_hwnd_| is deleted by its parent. void OnAccessibleHwndDeleted(); protected: - // AXTree methods. + // AXTreeDelegate methods. void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override; void OnAtomicUpdateFinished( @@ -73,14 +75,6 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin // TODO(dmazzoni): remove once http://crbug.com/113483 is fixed. BrowserAccessibilityWin* tracked_scroll_object_; - // Set to true if we need to fire a focus event on the root as soon as - // possible. - bool focus_event_on_root_needed_; - - // A flag to keep track of if we're inside the OnWindowFocused call stack - // so we don't keep calling it recursively. - bool inside_on_window_focused_; - DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerWin); }; diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc index 8fb3442d422..4038d66d063 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl.cc @@ -122,7 +122,7 @@ void BrowserAccessibilityStateImpl::UpdateHistograms() { switches::kForceRendererAccessibility)); } -#if !defined(OS_WIN) +#if !defined(OS_WIN) && !defined(OS_MACOSX) void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() { } #endif diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm b/chromium/content/browser/accessibility/browser_accessibility_state_impl_mac.mm new file mode 100644 index 00000000000..863627f0f2e --- /dev/null +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_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. + +#include "content/browser/accessibility/browser_accessibility_state_impl.h" + +#import + +#include "base/metrics/histogram.h" + +@interface NSWorkspace (Partials) + +@property(readonly) BOOL accessibilityDisplayShouldDifferentiateWithoutColor; +@property(readonly) BOOL accessibilityDisplayShouldIncreaseContrast; +@property(readonly) BOOL accessibilityDisplayShouldReduceTransparency; + +@end + +namespace content { + +void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() { + // NOTE: This function is running on the file thread. + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; + + SEL sel = @selector(accessibilityDisplayShouldIncreaseContrast); + if (![workspace respondsToSelector:sel]) + return; + + UMA_HISTOGRAM_BOOLEAN( + "Accessibility.Mac.DifferentiateWithoutColor", + workspace.accessibilityDisplayShouldDifferentiateWithoutColor); + UMA_HISTOGRAM_BOOLEAN( + "Accessibility.Mac.IncreaseContrast", + workspace.accessibilityDisplayShouldIncreaseContrast); + UMA_HISTOGRAM_BOOLEAN( + "Accessibility.Mac.ReduceTransparency", + workspace.accessibilityDisplayShouldReduceTransparency); + + UMA_HISTOGRAM_BOOLEAN( + "Accessibility.Mac.FullKeyboardAccessEnabled", + static_cast(NSApp).fullKeyboardAccessEnabled); +} + +} // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc index 1409b0f1ab5..1267770e9a2 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_state_impl_win.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/strings/string_util.h" diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.cc b/chromium/content/browser/accessibility/browser_accessibility_win.cc index 54165ed7323..98aeb77bc77 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win.cc @@ -20,6 +20,7 @@ #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/common/accessibility_messages.h" #include "content/public/common/content_client.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/ax_text_utils.h" #include "ui/base/win/accessibility_ids_win.h" #include "ui/base/win/accessibility_misc_utils.h" @@ -42,10 +43,6 @@ const GUID GUID_IAccessibleContentDocument = { const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter = L'\xfffc'; -// static -LONG BrowserAccessibilityWin::next_unique_id_win_ = - base::win::kFirstBrowserAccessibilityManagerAccessibilityId; - // // BrowserAccessibilityRelation // @@ -119,9 +116,8 @@ STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) { *n_targets = static_cast(target_ids_.size()); - BrowserAccessibilityManager* manager = owner_->manager(); for (long i = *n_targets - 1; i >= 0; --i) { - BrowserAccessibility* result = manager->GetFromID(target_ids_[i]); + BrowserAccessibilityWin* result = owner_->GetFromID(target_ids_[i]); if (!result || !result->instance_active()) { *n_targets = 0; break; @@ -143,14 +139,12 @@ STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index, return E_INVALIDARG; } - BrowserAccessibilityManager* manager = owner_->manager(); - BrowserAccessibility* result = - manager->GetFromID(target_ids_[target_index]); + BrowserAccessibility* result = owner_->GetFromID(target_ids_[target_index]); if (!result || !result->instance_active()) return E_FAIL; *target = static_cast( - result->ToBrowserAccessibilityWin()->NewReference()); + ToBrowserAccessibilityWin(result)->NewReference()); return S_OK; } @@ -207,29 +201,10 @@ BrowserAccessibility* BrowserAccessibility::Create() { return instance->NewReference(); } -const BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() - const { - return static_cast(this); -} - -BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() { - return static_cast(this); -} - BrowserAccessibilityWin::BrowserAccessibilityWin() : win_attributes_(new WinAttributes()), previous_scroll_x_(0), previous_scroll_y_(0) { - // Start unique IDs at -1 and decrement each time, because get_accChild - // uses positive IDs to enumerate children, so we use negative IDs to - // clearly distinguish between indices and unique IDs. - unique_id_win_ = next_unique_id_win_; - if (next_unique_id_win_ == - base::win::kLastBrowserAccessibilityManagerAccessibilityId) { - next_unique_id_win_ = - base::win::kFirstBrowserAccessibilityManagerAccessibilityId; - } - next_unique_id_win_--; } BrowserAccessibilityWin::~BrowserAccessibilityWin() { @@ -254,6 +229,10 @@ HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) { if (!target) return E_INVALIDARG; + // Return an error if it's not clickable. + if (!target->HasStringAttribute(ui::AX_ATTR_ACTION)) + return DISP_E_MEMBERNOTFOUND; + manager()->DoDefaultAction(*target); return S_OK; } @@ -281,7 +260,7 @@ STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, child->lVal = CHILDID_SELF; } else { child->vt = VT_DISPATCH; - child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference(); + child->pdispVal = ToBrowserAccessibilityWin(result)->NewReference(); } return S_OK; } @@ -355,7 +334,7 @@ STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir, } end->vt = VT_DISPATCH; - end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference(); + end->pdispVal = ToBrowserAccessibilityWin(result)->NewReference(); return S_OK; } @@ -435,7 +414,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) { return E_INVALIDARG; BrowserAccessibilityWin* focus = static_cast( - manager()->GetFocus(this)); + manager()->GetFocus()); if (focus == this) { focus_child->vt = VT_I4; focus_child->lVal = CHILDID_SELF; @@ -487,8 +466,16 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { return E_INVALIDARG; base::string16 name_str = target->name(); - if (name_str.empty()) - return S_FALSE; + if (name_str.empty()) { + if (target->ia2_role() == ROLE_SYSTEM_DOCUMENT && GetParent()) { + // Hack: Some versions of JAWS crash if they get an empty name on + // a document that's the child of an iframe, so always return a + // nonempty string for this role. https://crbug.com/583057 + name_str = L" "; + } else { + return S_FALSE; + } + } *name = SysAllocString(name_str.c_str()); @@ -503,7 +490,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) { if (!disp_parent) return E_INVALIDARG; - IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin(); + IAccessible* parent_obj = ToBrowserAccessibilityWin(GetParent()); if (parent_obj == NULL) { // This happens if we're the root of the tree; // return the IAccessible for the window. @@ -562,7 +549,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id, state->vt = VT_I4; state->lVal = target->ia_state(); - if (manager()->GetFocus(NULL) == this) + if (manager()->GetFocus() == this) state->lVal |= STATE_SYSTEM_FOCUSED; return S_OK; @@ -591,14 +578,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id, // Expose color well value. if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) { - int color = target->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE); - int red = (color >> 16) & 0xFF; - int green = (color >> 8) & 0xFF; - int blue = color & 0xFF; + unsigned int color = static_cast( + target->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); + unsigned int red = SkColorGetR(color); + unsigned int green = SkColorGetG(color); + unsigned int blue = SkColorGetB(color); base::string16 value_text; - value_text = base::IntToString16((red * 100) / 255) + L"% red " + - base::IntToString16((green * 100) / 255) + L"% green " + - base::IntToString16((blue * 100) / 255) + L"% blue"; + value_text = base::UintToString16(red * 100 / 255) + L"% red " + + base::UintToString16(green * 100 / 255) + L"% green " + + base::UintToString16(blue * 100 / 255) + L"% blue"; *value = SysAllocString(value_text.c_str()); DCHECK(*value); return S_OK; @@ -638,7 +626,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { selected->vt = VT_DISPATCH; selected->pdispVal = - InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference(); + ToBrowserAccessibilityWin(InternalGetChild(i))->NewReference(); return S_OK; } } @@ -653,7 +641,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { enum_variant->ItemAt(index)->vt = VT_DISPATCH; enum_variant->ItemAt(index)->pdispVal = - InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference(); + ToBrowserAccessibilityWin(InternalGetChild(i))->NewReference(); ++index; } } @@ -669,7 +657,7 @@ STDMETHODIMP BrowserAccessibilityWin::accSelect( return E_FAIL; if (flags_sel & SELFLAG_TAKEFOCUS) { - manager()->SetFocus(this, true); + manager()->SetFocus(*this); return S_OK; } @@ -702,19 +690,16 @@ STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) { } STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) { - if (!instance_active()) - return E_FAIL; - if (!attributes) return E_INVALIDARG; + *attributes = nullptr; + + if (!instance_active()) + return E_FAIL; - // The iaccessible2 attributes are a set of key-value pairs - // separated by semicolons, with a colon between the key and the value. base::string16 str; - const std::vector& attributes_list = ia2_attributes(); - for (unsigned int i = 0; i < attributes_list.size(); ++i) { - str += attributes_list[i] + L';'; - } + for (const base::string16& attribute : ia2_attributes()) + str += attribute + L';'; if (str.empty()) return S_FALSE; @@ -743,7 +728,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) { if (!unique_id) return E_INVALIDARG; - *unique_id = unique_id_win_; + *unique_id = -unique_id_; return S_OK; } @@ -862,7 +847,6 @@ STDMETHODIMP BrowserAccessibilityWin::scrollTo(IA2ScrollType scroll_type) { } manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this); - return S_OK; } @@ -1196,8 +1180,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column, GetIntListAttribute(ui::AX_ATTR_CELL_IDS); for (int i = 0; i < rows; ++i) { int cell_id = cell_ids[i * columns + column]; - BrowserAccessibilityWin* cell = static_cast( - manager()->GetFromID(cell_id)); + BrowserAccessibilityWin* cell = GetFromID(cell_id); if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { base::string16 cell_name = cell->GetString16Attribute( ui::AX_ATTR_NAME); @@ -1242,8 +1225,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt( const std::vector& cell_ids = GetIntListAttribute(ui::AX_ATTR_CELL_IDS); int cell_id = cell_ids[row * columns + column]; - BrowserAccessibilityWin* cell = static_cast( - manager()->GetFromID(cell_id)); + BrowserAccessibilityWin* cell = GetFromID(cell_id); int colspan; if (cell && cell->GetIntAttribute( @@ -1280,8 +1262,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index, return S_FALSE; int cell_id = unique_cell_ids[cell_index]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); int col_index; if (cell && cell->GetIntAttribute( @@ -1385,8 +1366,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row, GetIntListAttribute(ui::AX_ATTR_CELL_IDS); for (int i = 0; i < columns; ++i) { int cell_id = cell_ids[row * columns + i]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { base::string16 cell_name = cell->GetString16Attribute( ui::AX_ATTR_NAME); @@ -1430,8 +1410,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row, const std::vector& cell_ids = GetIntListAttribute(ui::AX_ATTR_CELL_IDS); int cell_id = cell_ids[row * columns + column]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); int rowspan; if (cell && cell->GetIntAttribute( @@ -1468,8 +1447,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index, return S_FALSE; int cell_id = unique_cell_ids[cell_index]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); int cell_row_index; if (cell && cell->GetIntAttribute( @@ -1597,8 +1575,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( return S_FALSE; int cell_id = unique_cell_ids[index]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); int rowspan; int colspan; if (cell && @@ -1756,8 +1733,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( for (int i = 0; i < rows; ++i) { int cell_id = cell_ids[i * columns + column]; - BrowserAccessibilityWin* cell = - manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* cell = GetFromID(cell_id); if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) (*n_column_header_cells)++; } @@ -1770,7 +1746,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( BrowserAccessibility* cell = manager()->GetFromID(cell_id); if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { (*cell_accessibles)[index] = static_cast( - cell->ToBrowserAccessibilityWin()->NewReference()); + ToBrowserAccessibilityWin(cell)->NewReference()); ++index; } } @@ -1867,7 +1843,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( BrowserAccessibility* cell = manager()->GetFromID(cell_id); if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { (*cell_accessibles)[index] = static_cast( - cell->ToBrowserAccessibilityWin()->NewReference()); + ToBrowserAccessibilityWin(cell)->NewReference()); ++index; } } @@ -1962,7 +1938,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) { } *table = static_cast( - find_table->ToBrowserAccessibilityWin()->NewReference()); + ToBrowserAccessibilityWin(find_table)->NewReference()); return S_OK; } @@ -2364,15 +2340,40 @@ STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index, return S_OK; } -// -// IAccessibleText methods not implemented. -// - STDMETHODIMP BrowserAccessibilityWin::get_attributes(LONG offset, LONG* start_offset, LONG* end_offset, BSTR* text_attributes) { - return E_NOTIMPL; + if (!start_offset || !end_offset || !text_attributes) + return E_INVALIDARG; + + *start_offset = *end_offset = 0; + *text_attributes = nullptr; + if (!instance_active()) + return E_FAIL; + + const base::string16& text = GetText(); + HandleSpecialTextOffset(text, &offset); + if (offset < 0 || offset > static_cast(text.size())) + return E_INVALIDARG; + + ComputeStylesIfNeeded(); + *start_offset = FindStartOfStyle(offset, ui::BACKWARDS_DIRECTION); + *end_offset = FindStartOfStyle(offset, ui::FORWARDS_DIRECTION); + + base::string16 attributes_str; + const std::vector& attributes = + offset_to_text_attributes().find(*start_offset)->second; + for (const base::string16& attribute : attributes) { + attributes_str += attribute + L';'; + } + + if (attributes.empty()) + return S_FALSE; + + *text_attributes = SysAllocString(attributes_str.c_str()); + DCHECK(*text_attributes); + return S_OK; } // @@ -2403,14 +2404,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( } int32_t id = hyperlinks()[index]; - BrowserAccessibilityWin* child = - manager()->GetFromID(id)->ToBrowserAccessibilityWin(); - if (child) { - *hyperlink = static_cast(child->NewReference()); - return S_OK; - } + BrowserAccessibilityWin* link = GetFromID(id); + if (!link) + return E_FAIL; - return E_FAIL; + *hyperlink = static_cast(link->NewReference()); + return S_OK; } STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( @@ -2502,7 +2501,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_startIndex(long* index) { const auto parent = GetParent(); if (parent) { hypertext_offset = - parent->ToBrowserAccessibilityWin()->GetHypertextOffsetFromChild(*this); + ToBrowserAccessibilityWin(parent)->GetHypertextOffsetFromChild(*this); } *index = static_cast(hypertext_offset); return S_OK; @@ -2743,7 +2742,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo( *name_space_id = 0; *node_value = SysAllocString(value().c_str()); *num_children = PlatformChildCount(); - *unique_id = unique_id_win_; + *unique_id = -unique_id_; if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || GetRole() == ui::AX_ROLE_WEB_AREA) { @@ -2881,7 +2880,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) { if (!node) return E_INVALIDARG; - *node = GetParent()->ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin(GetParent())->NewReference(); return S_OK; } @@ -2897,7 +2896,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) { return S_FALSE; } - *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin(PlatformGetChild(0))->NewReference(); return S_OK; } @@ -2913,8 +2912,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) { return S_FALSE; } - *node = PlatformGetChild(PlatformChildCount() - 1) - ->ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin( + PlatformGetChild(PlatformChildCount() - 1))->NewReference(); return S_OK; } @@ -2931,8 +2930,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_previousSibling( return S_FALSE; } - *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)-> - ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin( + GetParent()->InternalGetChild(GetIndexInParent() - 1))->NewReference(); return S_OK; } @@ -2951,8 +2950,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) { return S_FALSE; } - *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)-> - ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin( + GetParent()->InternalGetChild(GetIndexInParent() + 1))->NewReference(); return S_OK; } @@ -2974,7 +2973,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_childAt( return S_FALSE; } - *node = child->ToBrowserAccessibilityWin()->NewReference(); + *node = ToBrowserAccessibilityWin(child)->NewReference(); return S_OK; } @@ -2988,7 +2987,20 @@ BrowserAccessibilityWin::get_localInterface(void** local_interface) { } STDMETHODIMP BrowserAccessibilityWin::get_language(BSTR* language) { - return E_NOTIMPL; + if (!language) + return E_INVALIDARG; + *language = nullptr; + + if (!instance_active()) + return E_FAIL; + + base::string16 lang = GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE); + if (lang.empty()) + lang = L"en-US"; + + *language = SysAllocString(lang.c_str()); + DCHECK(*language); + return S_OK; } // @@ -3067,7 +3079,21 @@ STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring( } STDMETHODIMP BrowserAccessibilityWin::get_fontFamily(BSTR* font_family) { - return E_NOTIMPL; + if (!font_family) + return E_INVALIDARG; + *font_family = nullptr; + + if (!instance_active()) + return E_FAIL; + + base::string16 family = + GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY); + if (family.empty()) + return S_FALSE; + + *font_family = SysAllocString(family.c_str()); + DCHECK(*font_family); + return S_OK; } // @@ -3093,7 +3119,7 @@ STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guid_service, BrowserAccessibility* node = this; while (node->GetParent()) node = node->GetParent()->manager()->GetRoot(); - return node->ToBrowserAccessibilityWin()->QueryInterface( + return ToBrowserAccessibilityWin(node)->QueryInterface( IID_IAccessible2, object); } @@ -3248,6 +3274,45 @@ HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( this_ptr, entries, iid, object); } +void BrowserAccessibilityWin::ComputeStylesIfNeeded() { + if (!offset_to_text_attributes().empty()) + return; + + std::map> attributes_map; + if (PlatformIsLeaf()) { + attributes_map[0] = ComputeTextAttributes(); + win_attributes_->offset_to_text_attributes.swap(attributes_map); + return; + } + + int start_offset = 0; + for (size_t i = 0; i < PlatformChildCount(); ++i) { + const auto child = ToBrowserAccessibilityWin(PlatformGetChild(i)); + DCHECK(child); + std::vector attributes(child->ComputeTextAttributes()); + + if (attributes_map.empty()) { + attributes_map[start_offset] = attributes; + } else { + // Only add the attributes for this child if we are at the start of a new + // style span. + std::vector previous_attributes = + attributes_map.rbegin()->second; + if (!std::equal(attributes.begin(), attributes.end(), + previous_attributes.begin())) { + attributes_map[start_offset] = attributes; + } + } + + if (child->IsTextOnlyObject()) + start_offset += child->GetText().length(); + else + start_offset += 1; + } + + win_attributes_->offset_to_text_attributes.swap(attributes_map); +} + base::string16 BrowserAccessibilityWin::GetText() const { if (PlatformIsChildOfLeaf()) return BrowserAccessibility::GetText(); @@ -3308,6 +3373,13 @@ void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() { BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY, "container-busy"); + // Expose the non-standard explicit-name IA2 attribute. + int name_from; + if (GetIntAttribute(ui::AX_ATTR_NAME_FROM, &name_from) && + name_from != ui::AX_NAME_FROM_CONTENTS) { + win_attributes_->ia2_attributes.push_back(L"explicit-name:true"); + } + // Expose table cell index. if (IsCellOrTableHeaderRole()) { BrowserAccessibility* table = GetParent(); @@ -3325,44 +3397,6 @@ void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() { } } - // Expose invalid state for form controls and elements with aria-invalid. - int invalid_state; - if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE, &invalid_state)) { - // TODO(nektar): Handle the possibility of having multiple aria-invalid - // attributes defined, e.g., "invalid:spelling,grammar". - switch (invalid_state) { - case ui::AX_INVALID_STATE_FALSE: - win_attributes_->ia2_attributes.push_back(L"invalid:false"); - break; - case ui::AX_INVALID_STATE_TRUE: - win_attributes_->ia2_attributes.push_back(L"invalid:true"); - break; - case ui::AX_INVALID_STATE_SPELLING: - win_attributes_->ia2_attributes.push_back(L"invalid:spelling"); - break; - case ui::AX_INVALID_STATE_GRAMMAR: - win_attributes_->ia2_attributes.push_back(L"invalid:grammar"); - break; - case ui::AX_INVALID_STATE_OTHER: - { - base::string16 aria_invalid_value; - if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE, - &aria_invalid_value)) { - SanitizeStringAttributeForIA2(aria_invalid_value, - &aria_invalid_value); - win_attributes_->ia2_attributes.push_back( - L"invalid:" + aria_invalid_value); - } else { - // Set the attribute to L"true", since we cannot be more specific. - win_attributes_->ia2_attributes.push_back(L"invalid:true"); - } - } - break; - default: - NOTREACHED(); - } - } - // Expose row or column header sort direction. int32_t sort_direction; if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER || @@ -3442,8 +3476,7 @@ void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() { // the character index of each embedded object character to the id of the // child object it points to. for (unsigned int i = 0; i < PlatformChildCount(); ++i) { - BrowserAccessibilityWin* child = - PlatformGetChild(i)->ToBrowserAccessibilityWin(); + const auto child = ToBrowserAccessibilityWin(PlatformGetChild(i)); DCHECK(child); // Similar to Firefox, we don't expose text-only objects in IA2 hypertext. if (child->IsTextOnlyObject()) { @@ -3547,18 +3580,22 @@ void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation) { // Changing a static text node can affect the IAccessibleText hypertext // of the parent node, so force an update on the parent. - BrowserAccessibilityWin* parent = GetParent()->ToBrowserAccessibilityWin(); + BrowserAccessibilityWin* parent = ToBrowserAccessibilityWin(GetParent()); if (parent && IsTextOnlyObject() && name() != old_win_attributes_->name) { - parent->UpdateStep1ComputeWinAttributes(); - parent->UpdateStep2ComputeHypertext(); - parent->UpdateStep3FireEvents(false); + parent->UpdatePlatformAttributes(); } } old_win_attributes_.reset(nullptr); } +void BrowserAccessibilityWin::UpdatePlatformAttributes() { + UpdateStep1ComputeWinAttributes(); + UpdateStep2ComputeHypertext(); + UpdateStep3FireEvents(false); +} + void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() { manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent( EVENT_OBJECT_HIDE, this); @@ -3581,6 +3618,182 @@ void BrowserAccessibilityWin::OnLocationChanged() { EVENT_OBJECT_LOCATIONCHANGE, this); } +std::vector BrowserAccessibilityWin::ComputeTextAttributes() + const { + std::vector attributes; + + // We include list markers for now, but there might be other objects that are + // auto generated. + // TODO(nektar): Compute what objects are auto-generated in Blink. + if (GetRole() == ui::AX_ROLE_LIST_MARKER) + attributes.push_back(L"auto-generated:true"); + else + attributes.push_back(L"auto-generated:false"); + + int color; + base::string16 color_value(L"transparent"); + if (GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, &color)) { + unsigned int alpha = SkColorGetA(color); + unsigned int red = SkColorGetR(color); + unsigned int green = SkColorGetG(color); + unsigned int blue = SkColorGetB(color); + if (alpha) { + color_value = L"rgb(" + base::UintToString16(red) + L',' + + base::UintToString16(green) + L',' + + base::UintToString16(blue) + L')'; + } + } + SanitizeStringAttributeForIA2(color_value, &color_value); + attributes.push_back(L"background-color:" + color_value); + + if (GetIntAttribute(ui::AX_ATTR_COLOR, &color)) { + unsigned int red = SkColorGetR(color); + unsigned int green = SkColorGetG(color); + unsigned int blue = SkColorGetB(color); + color_value = L"rgb(" + base::UintToString16(red) + L',' + + base::UintToString16(green) + L',' + + base::UintToString16(blue) + L')'; + } else { + color_value = L"rgb(0,0,0)"; + } + SanitizeStringAttributeForIA2(color_value, &color_value); + attributes.push_back(L"color:" + color_value); + + base::string16 font_family( + GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY)); + // Attribute has no default value. + if (!font_family.empty()) { + SanitizeStringAttributeForIA2(font_family, &font_family); + attributes.push_back(L"font-family:" + font_family); + } + + float font_size; + // Attribute has no default value. + if (GetFloatAttribute(ui::AX_ATTR_FONT_SIZE, &font_size)) { + // The IA2 Spec requires the value to be in pt, not in pixels. + // There are 72 points per inch. + // We assume that there are 96 pixels per inch on a standard display. + // TODO(nektar): Figure out the current value of pixels per inch. + float points = font_size * 72.0 / 96.0; + attributes.push_back(L"font-size:" + + base::UTF8ToUTF16(base::DoubleToString(points)) + + L"pt"); + } + + auto text_style = + static_cast(GetIntAttribute(ui::AX_ATTR_TEXT_STYLE)); + if (text_style == ui::AX_TEXT_STYLE_NONE) { + attributes.push_back(L"font-style:normal"); + attributes.push_back(L"font-weight:normal"); + } else { + if (text_style & ui::AX_TEXT_STYLE_BOLD) + attributes.push_back(L"font-weight:bold"); + + base::string16 font_style; + if (text_style & ui::AX_TEXT_STYLE_ITALIC) + font_style += L",italic"; + if (text_style & ui::AX_TEXT_STYLE_UNDERLINE) + font_style += L",underline"; + if (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) + font_style += L",line-through"; + // TODO(nektar): Support more font style attributes in Blink. + + if (font_style.empty()) { + font_style = L"normal"; + } else { + // Remove the leading comma. + font_style.erase(0, 1); + } + attributes.push_back(L"font-style:" + font_style); + } + + auto invalid_state = static_cast( + GetIntAttribute(ui::AX_ATTR_INVALID_STATE)); + switch (invalid_state) { + case ui::AX_INVALID_STATE_NONE: + case ui::AX_INVALID_STATE_FALSE: + attributes.push_back(L"invalid:false"); + break; + case ui::AX_INVALID_STATE_TRUE: + attributes.push_back(L"invalid:true"); + break; + case ui::AX_INVALID_STATE_SPELLING: + case ui::AX_INVALID_STATE_GRAMMAR: { + base::string16 spelling_grammar_value; + if (invalid_state & ui::AX_INVALID_STATE_SPELLING) + spelling_grammar_value = L"spelling"; + else if (invalid_state & ui::AX_INVALID_STATE_GRAMMAR) + spelling_grammar_value = L"grammar"; + else + spelling_grammar_value = L"spelling,grammar"; + attributes.push_back(L"invalid:" + spelling_grammar_value); + break; + } + case ui::AX_INVALID_STATE_OTHER: { + base::string16 aria_invalid_value; + if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE, + &aria_invalid_value)) { + SanitizeStringAttributeForIA2(aria_invalid_value, &aria_invalid_value); + attributes.push_back(L"invalid:" + aria_invalid_value); + } else { + // Set the attribute to L"true", since we cannot be more specific. + attributes.push_back(L"invalid:true"); + } + break; + } + default: + NOTREACHED(); + } + + base::string16 language(GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); + // Default value should be L"en-US". + if (language.empty()) { + attributes.push_back(L"language:en-US"); + } else { + SanitizeStringAttributeForIA2(language, &language); + attributes.push_back(L"language:" + language); + } + + // TODO(nektar): Add Blink support for the following attributes. + // Currently set to their default values as dictated by the IA2 Spec. + attributes.push_back(L"text-line-through-mode:continuous"); + attributes.push_back(L"text-line-through-style:none"); + // Default value must be the empty string. + attributes.push_back(L"text-line-through-text:"); + attributes.push_back(L"text-line-through-type:none"); + attributes.push_back(L"text-line-through-width:auto"); + attributes.push_back(L"text-outline:false"); + attributes.push_back(L"text-position:baseline"); + attributes.push_back(L"text-shadow:none"); + attributes.push_back(L"text-underline-mode:continuous"); + attributes.push_back(L"text-underline-style:none"); + attributes.push_back(L"text-underline-type:none"); + attributes.push_back(L"text-underline-width:auto"); + + auto text_direction = static_cast( + GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)); + switch (text_direction) { + case ui::AX_TEXT_DIRECTION_NONE: + case ui::AX_TEXT_DIRECTION_LTR: + attributes.push_back(L"writing-mode:lr"); + break; + case ui::AX_TEXT_DIRECTION_RTL: + attributes.push_back(L"writing-mode:rl"); + break; + case ui::AX_TEXT_DIRECTION_TTB: + attributes.push_back(L"writing-mode:tb"); + break; + case ui::AX_TEXT_DIRECTION_BTT: + // Not listed in the IA2 Spec. + attributes.push_back(L"writing-mode:bt"); + break; + default: + NOTREACHED(); + } + + return attributes; +} + BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() { AddRef(); return this; @@ -3589,17 +3802,21 @@ BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() { BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID( const VARIANT& var_id) { if (var_id.vt != VT_I4) - return NULL; + return nullptr; LONG child_id = var_id.lVal; if (child_id == CHILDID_SELF) return this; if (child_id >= 1 && child_id <= static_cast(PlatformChildCount())) - return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin(); + return ToBrowserAccessibilityWin(PlatformGetChild(child_id - 1)); - return manager()->ToBrowserAccessibilityManagerWin()-> - GetFromUniqueIdWin(child_id); + BrowserAccessibilityWin* child = ToBrowserAccessibilityWin( + BrowserAccessibility::GetFromUniqueID(-child_id)); + if (child && child->IsDescendantOf(this)) + return child; + + return nullptr; } HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr( @@ -3672,7 +3889,7 @@ bool BrowserAccessibilityWin::IsHyperlink() const { const auto parent = GetParent(); if (parent) { hyperlink_index = - parent->ToBrowserAccessibilityWin()->GetHyperlinkIndexFromChild(*this); + ToBrowserAccessibilityWin(parent)->GetHyperlinkIndexFromChild(*this); } if (hyperlink_index >= 0) @@ -3680,6 +3897,23 @@ bool BrowserAccessibilityWin::IsHyperlink() const { return false; } +BrowserAccessibilityWin* +BrowserAccessibilityWin::GetHyperlinkFromHypertextOffset(int offset) const { + std::map::iterator iterator = + hyperlink_offset_to_index().find(offset); + if (iterator == hyperlink_offset_to_index().end()) + return nullptr; + + int32_t index = iterator->second; + DCHECK_GE(index, 0); + DCHECK_LT(index, static_cast(hyperlinks().size())); + int32_t id = hyperlinks()[index]; + BrowserAccessibilityWin* hyperlink = GetFromID(id); + if (!hyperlink) + return nullptr; + return hyperlink; +} + int32_t BrowserAccessibilityWin::GetHyperlinkIndexFromChild( const BrowserAccessibilityWin& child) const { if (hyperlinks().empty()) @@ -3719,7 +3953,7 @@ int32_t BrowserAccessibilityWin::GetHypertextOffsetFromChild( DCHECK_LT(index_in_parent, static_cast(InternalChildCount())); for (uint32_t i = 0; i < static_cast(index_in_parent); ++i) { const BrowserAccessibilityWin* sibling = - InternalGetChild(i)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(InternalGetChild(i)); DCHECK(sibling); if (sibling->IsTextOnlyObject()) hypertextOffset += sibling->GetText().size(); @@ -3738,11 +3972,11 @@ int32_t BrowserAccessibilityWin::GetHypertextOffsetFromChild( int32_t BrowserAccessibilityWin::GetHypertextOffsetFromDescendant( const BrowserAccessibilityWin& descendant) const { - auto parent_object = descendant.GetParent()->ToBrowserAccessibilityWin(); + auto parent_object = ToBrowserAccessibilityWin(descendant.GetParent()); auto current_object = const_cast(&descendant); while (parent_object && parent_object != this) { current_object = parent_object; - parent_object = current_object->GetParent()->ToBrowserAccessibilityWin(); + parent_object = ToBrowserAccessibilityWin(current_object->GetParent()); } if (!parent_object) return -1; @@ -3823,8 +4057,7 @@ int BrowserAccessibilityWin::GetHypertextOffsetFromEndpoint( int BrowserAccessibilityWin::GetSelectionAnchor() const { int32_t anchor_id = manager()->GetTreeData().sel_anchor_object_id; - const auto anchor_object = - manager()->GetFromID(anchor_id)->ToBrowserAccessibilityWin(); + const BrowserAccessibilityWin* anchor_object = GetFromID(anchor_id); if (!anchor_object) return -1; @@ -3834,8 +4067,7 @@ int BrowserAccessibilityWin::GetSelectionAnchor() const { int BrowserAccessibilityWin::GetSelectionFocus() const { int32_t focus_id = manager()->GetTreeData().sel_focus_object_id; - const auto focus_object = - manager()->GetFromID(focus_id)->ToBrowserAccessibilityWin(); + const BrowserAccessibilityWin* focus_object = GetFromID(focus_id); if (!focus_object) return -1; @@ -3847,8 +4079,7 @@ void BrowserAccessibilityWin::GetSelectionOffsets( int* selection_start, int* selection_end) const { DCHECK(selection_start && selection_end); - if (HasState(ui::AX_STATE_EDITABLE) && - !HasState(ui::AX_STATE_RICHLY_EDITABLE) && + if (IsSimpleTextControl() && GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { return; @@ -3885,22 +4116,13 @@ void BrowserAccessibilityWin::GetSelectionOffsets( // the selection. int* largest_offset = (*selection_start <= *selection_end) ? selection_end : selection_start; - auto current_object = const_cast(this); - LONG hyperlink_index; - HRESULT hr = - current_object->get_hyperlinkIndex(*largest_offset, &hyperlink_index); - if (hr != S_OK) + BrowserAccessibilityWin* hyperlink = + GetHyperlinkFromHypertextOffset(*largest_offset); + if (!hyperlink) return; - DCHECK_GE(hyperlink_index, 0); - base::win::ScopedComPtr hyperlink; - hr = current_object->get_hyperlink(hyperlink_index, hyperlink.Receive()); - DCHECK(SUCCEEDED(hr)); - base::win::ScopedComPtr hyperlink_text; - hr = hyperlink.QueryInterface(hyperlink_text.Receive()); - DCHECK(SUCCEEDED(hr)); LONG n_selections = 0; - hr = hyperlink_text->get_nSelections(&n_selections); + HRESULT hr = hyperlink->get_nSelections(&n_selections); DCHECK(SUCCEEDED(hr)); if (n_selections > 0) ++(*largest_offset); @@ -4032,8 +4254,40 @@ LONG BrowserAccessibilityWin::FindBoundary( text, line_breaks, boundary, start_offset, direction); } -BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32_t id) { - return manager()->GetFromID(id)->ToBrowserAccessibilityWin(); +LONG BrowserAccessibilityWin::FindStartOfStyle( + LONG start_offset, + ui::TextBoundaryDirection direction) const { + LONG text_length = static_cast(GetText().length()); + DCHECK_GE(start_offset, 0); + DCHECK_LE(start_offset, text_length); + + switch (direction) { + case ui::BACKWARDS_DIRECTION: { + if (offset_to_text_attributes().empty()) + return 0; + + auto iterator = offset_to_text_attributes().upper_bound(start_offset); + --iterator; + return static_cast(iterator->first); + } + case ui::FORWARDS_DIRECTION: { + const auto iterator = + offset_to_text_attributes().upper_bound(start_offset); + if (iterator == offset_to_text_attributes().end()) + return text_length; + return static_cast(iterator->first); + } + default: + NOTREACHED(); + } + + return start_offset; +} + +BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32_t id) const { + if (!instance_active()) + return nullptr; + return ToBrowserAccessibilityWin(manager()->GetFromID(id)); } bool BrowserAccessibilityWin::IsListBoxOptionOrMenuListOption() { @@ -4056,6 +4310,25 @@ bool BrowserAccessibilityWin::IsListBoxOptionOrMenuListOption() { return false; } +void BrowserAccessibilityWin::AddRelations( + ui::AXIntListAttribute src_attr, + const base::string16& iaccessiblerelation_type) { + if (!HasIntListAttribute(src_attr)) + return; + + const std::vector& ids = GetIntListAttribute(src_attr); + for (size_t i = 0; i < ids.size(); ++i) { + CComObject* relation; + HRESULT hr = + CComObject::CreateInstance(&relation); + DCHECK(SUCCEEDED(hr)); + relation->AddRef(); + relation->Initialize(this, iaccessiblerelation_type); + relation->AddTarget(ids[i]); + relations_.push_back(relation); + } +} + void BrowserAccessibilityWin::UpdateRequiredAttributes() { // Expose slider value. if (ia_role() == ROLE_SYSTEM_PROGRESSBAR || @@ -4119,25 +4392,6 @@ void BrowserAccessibilityWin::UpdateRequiredAttributes() { } } -void BrowserAccessibilityWin::AddRelations( - ui::AXIntListAttribute src_attr, - const base::string16& iaccessiblerelation_type) { - if (!HasIntListAttribute(src_attr)) - return; - - const std::vector& ids = GetIntListAttribute(src_attr); - for (size_t i = 0; i < ids.size(); ++i) { - CComObject* relation; - HRESULT hr = CComObject::CreateInstance( - &relation); - DCHECK(SUCCEEDED(hr)); - relation->AddRef(); - relation->Initialize(this, iaccessiblerelation_type); - relation->AddTarget(ids[i]); - relations_.push_back(relation); - } -} - void BrowserAccessibilityWin::InitRoleAndState() { int32_t ia_role = 0; int32_t ia_state = 0; @@ -4332,8 +4586,15 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia_state |= STATE_SYSTEM_FOCUSABLE; break; case ui::AX_ROLE_EMBEDDED_OBJECT: - ia_role = ROLE_SYSTEM_CLIENT; - ia2_role = IA2_ROLE_EMBEDDED_OBJECT; + if (HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { + // Windows screen readers assume that IA2_ROLE_EMBEDDED_OBJECT + // doesn't have any children, but it may be something like a + // browser plugin that has a document inside. + ia_role = ROLE_SYSTEM_GROUPING; + } else { + ia_role = ROLE_SYSTEM_CLIENT; + ia2_role = IA2_ROLE_EMBEDDED_OBJECT; + } break; case ui::AX_ROLE_FIGCAPTION: role_name = html_tag; @@ -4420,8 +4681,6 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia_role = ROLE_SYSTEM_LISTITEM; if (ia_state & STATE_SYSTEM_SELECTABLE) { ia_state |= STATE_SYSTEM_FOCUSABLE; - if (HasState(ui::AX_STATE_FOCUSED)) - ia_state |= STATE_SYSTEM_FOCUSED; } break; case ui::AX_ROLE_LIST_ITEM: @@ -4470,8 +4729,6 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia2_state &= ~(IA2_STATE_EDITABLE); if (ia_state & STATE_SYSTEM_SELECTABLE) { ia_state |= STATE_SYSTEM_FOCUSABLE; - if (HasState(ui::AX_STATE_FOCUSED)) - ia_state |= STATE_SYSTEM_FOCUSED; } break; case ui::AX_ROLE_METER: @@ -4616,6 +4873,7 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia2_state |= IA2_STATE_SINGLE_LINE; ia2_state |= IA2_STATE_SELECTABLE_TEXT; break; + case ui::AX_ROLE_ABBR: case ui::AX_ROLE_TIME: role_name = html_tag; ia_role = ROLE_SYSTEM_TEXT; @@ -4691,4 +4949,15 @@ void BrowserAccessibilityWin::InitRoleAndState() { win_attributes_->ia2_state = ia2_state; } +BrowserAccessibilityWin* ToBrowserAccessibilityWin(BrowserAccessibility* obj) { + DCHECK(!obj || obj->IsNative()); + return static_cast(obj); +} + +const BrowserAccessibilityWin* +ToBrowserAccessibilityWin(const BrowserAccessibility* obj) { + DCHECK(!obj || obj->IsNative()); + return static_cast(obj); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/browser_accessibility_win.h b/chromium/content/browser/accessibility/browser_accessibility_win.h index adeb16b494b..0a4648379af 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_win.h @@ -97,17 +97,17 @@ BrowserAccessibilityWin CONTENT_EXPORT ~BrowserAccessibilityWin() override; - // The Windows-specific unique ID, used as the child ID for MSAA methods - // like NotifyWinEvent, and as the unique ID for IAccessible2 and ISimpleDOM. - LONG unique_id_win() const { return unique_id_win_; } - // Called after an atomic tree update completes. See // BrowserAccessibilityManagerWin::OnAtomicUpdateFinished for more // details on what these do. CONTENT_EXPORT void UpdateStep1ComputeWinAttributes(); CONTENT_EXPORT void UpdateStep2ComputeHypertext(); CONTENT_EXPORT void UpdateStep3FireEvents(bool is_subtree_creation); - CONTENT_EXPORT void UpdateStep4DeleteOldWinAttributes(); + + // This is used to call UpdateStep1ComputeWinAttributes, ... above when + // a node needs to be updated for some other reason other than via + // OnAtomicUpdateFinished. + CONTENT_EXPORT void UpdatePlatformAttributes() override; // // BrowserAccessibility methods. @@ -713,6 +713,10 @@ BrowserAccessibilityWin REFIID iid, void** object); + // Computes and caches the IA2 text style attributes for the text and other + // embedded child objects. + CONTENT_EXPORT void ComputeStylesIfNeeded(); + CONTENT_EXPORT base::string16 GetText() const override; // Accessors. @@ -727,6 +731,10 @@ BrowserAccessibilityWin base::string16 name() const { return win_attributes_->name; } base::string16 description() const { return win_attributes_->description; } base::string16 value() const { return win_attributes_->value; } + const std::map>& offset_to_text_attributes() + const { + return win_attributes_->offset_to_text_attributes; + } std::map& hyperlink_offset_to_index() const { return win_attributes_->hyperlink_offset_to_index; } @@ -735,6 +743,9 @@ BrowserAccessibilityWin } private: + // Returns the IA2 text attributes for this object. + std::vector ComputeTextAttributes() const; + // Add one to the reference count and return the same object. Always // use this method when returning a BrowserAccessibilityWin object as // an output parameter to a COM interface, never use it otherwise. @@ -790,6 +801,9 @@ BrowserAccessibilityWin // Returns true if the current object is an IA2 hyperlink. bool IsHyperlink() const; + // Returns the hyperlink at the given text position, or nullptr if no + // hyperlink can be found. + BrowserAccessibilityWin* GetHyperlinkFromHypertextOffset(int offset) const; // Functions for retrieving offsets for hyperlinks and hypertext. // Return -1 in case of failure. @@ -851,26 +865,31 @@ BrowserAccessibilityWin LONG start_offset, ui::TextBoundaryDirection direction); - // Return a pointer to the object corresponding to the given id, - // does not make a new reference. - BrowserAccessibilityWin* GetFromID(int32_t id); + // Searches forward from the given offset until the start of the next style + // is found, or searches backward from the given offset until the start of the + // current style is found. + LONG FindStartOfStyle(LONG start_offset, + ui::TextBoundaryDirection direction) const; + + // ID refers to the node ID in the current tree, not the globally unique ID. + // TODO(nektar): Could we use globally unique IDs everywhere? + // TODO(nektar): Rename this function to GetFromNodeID. + BrowserAccessibilityWin* GetFromID(int32_t id) const; // Returns true if this is a list box option with a parent of type list box, // or a menu list option with a parent of type menu list popup. bool IsListBoxOptionOrMenuListOption(); - // Updates object attributes of IA2 with html attributes. - void UpdateRequiredAttributes(); - // Given an int list attribute containing the ids of related elements, // add a new IAccessibleRelation for this object with the given type name. void AddRelations(ui::AXIntListAttribute src_attr, const base::string16& iaccessiblerelation_type); - // Windows-specific unique ID (unique within the browser process), - // used for get_accChild, NotifyWinEvent, and as the unique ID for - // IAccessible2 and ISimpleDOM. - LONG unique_id_win_; + // Updates object attributes of IA2 with html attributes. + void UpdateRequiredAttributes(); + + // Updates the IA2 text style attributes. + void UpdateTextAttributes(); struct WinAttributes { WinAttributes(); @@ -896,6 +915,9 @@ BrowserAccessibilityWin // Hypertext. base::string16 hypertext; + // Maps each style span to its start offset in hypertext. + std::map> offset_to_text_attributes; + // Maps the |hypertext_| embedded character offset to an index in // |hyperlinks_|. std::map hyperlink_offset_to_index; @@ -918,9 +940,6 @@ BrowserAccessibilityWin int previous_scroll_x_; int previous_scroll_y_; - // The next unique id to use. - static LONG next_unique_id_win_; - // Give BrowserAccessibility::Create access to our constructor. friend class BrowserAccessibility; friend class BrowserAccessibilityRelation; @@ -928,6 +947,12 @@ BrowserAccessibilityWin DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityWin); }; +CONTENT_EXPORT BrowserAccessibilityWin* +ToBrowserAccessibilityWin(BrowserAccessibility* obj); + +CONTENT_EXPORT const BrowserAccessibilityWin* +ToBrowserAccessibilityWin(const BrowserAccessibility* obj); + } // namespace content #endif // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_WIN_H_ diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc index aac1961cb1d..e8c236a5195 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc @@ -161,8 +161,7 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) { MakeAXTreeUpdate(root, button, checkbox), NULL, new CountedBrowserAccessibilityFactory())); ASSERT_EQ(3, CountedBrowserAccessibility::num_instances()); - IAccessible* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); + IAccessible* root_accessible = ToBrowserAccessibilityWin(manager->GetRoot()); IDispatch* root_iaccessible = NULL; IDispatch* child1_iaccessible = NULL; base::win::ScopedVariant childid_self(CHILDID_SELF); @@ -215,7 +214,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) { // value. base::win::ScopedVariant one(1); base::win::ScopedComPtr text_dispatch; - HRESULT hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild( + HRESULT hr = ToBrowserAccessibilityWin(manager->GetRoot())->get_accChild( one, text_dispatch.Receive()); ASSERT_EQ(S_OK, hr); @@ -249,7 +248,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) { // Query for the text IAccessible and verify that it now returns "new text" // as its value. - hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild( + hr = ToBrowserAccessibilityWin(manager->GetRoot())->get_accChild( one, text_dispatch.Receive()); ASSERT_EQ(S_OK, hr); @@ -396,12 +395,12 @@ TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { ASSERT_EQ(7, CountedBrowserAccessibility::num_instances()); BrowserAccessibilityWin* root_obj = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); ASSERT_NE(nullptr, root_obj); ASSERT_EQ(1U, root_obj->PlatformChildCount()); BrowserAccessibilityWin* text_field_obj = - root_obj->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_obj->PlatformGetChild(0)); ASSERT_NE(nullptr, text_field_obj); long text_len; @@ -516,7 +515,7 @@ TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) { ASSERT_EQ(3, CountedBrowserAccessibility::num_instances()); BrowserAccessibilityWin* root_obj = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); long text_len; EXPECT_EQ(S_OK, root_obj->get_nCharacters(&text_len)); @@ -637,7 +636,7 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { ASSERT_EQ(9, CountedBrowserAccessibility::num_instances()); BrowserAccessibilityWin* root_obj = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); long text_len; EXPECT_EQ(S_OK, root_obj->get_nCharacters(&text_len)); @@ -821,8 +820,8 @@ TEST_F(BrowserAccessibilityTest, EmptyDocHasUniqueIdWin) { 1 << ui::AX_STATE_ENABLED, root->GetState()); - LONG unique_id_win = root->ToBrowserAccessibilityWin()->unique_id_win(); - ASSERT_EQ(root, manager->GetFromUniqueIdWin(unique_id_win)); + int32_t unique_id = ToBrowserAccessibilityWin(root)->unique_id(); + ASSERT_EQ(root, BrowserAccessibility::GetFromUniqueID(unique_id)); } TEST_F(BrowserAccessibilityTest, TestIA2Attributes) { @@ -855,13 +854,13 @@ TEST_F(BrowserAccessibilityTest, TestIA2Attributes) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); - ASSERT_NE(nullptr, root_accessible); - ASSERT_EQ(2U, root_accessible->PlatformChildCount()); + ToBrowserAccessibilityWin(manager->GetRoot()); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(2U, root_accessible->PlatformChildCount()); - BrowserAccessibilityWin* pseudo_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); - ASSERT_NE(nullptr, pseudo_accessible); + BrowserAccessibilityWin* pseudo_accessible = + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); + ASSERT_NE(nullptr, pseudo_accessible); base::win::ScopedBstr attributes; HRESULT hr = pseudo_accessible->get_attributes(attributes.Receive()); @@ -871,7 +870,7 @@ TEST_F(BrowserAccessibilityTest, TestIA2Attributes) { EXPECT_EQ(L"display:none;tag:;", attributes_str); BrowserAccessibilityWin* checkbox_accessible = - root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, checkbox_accessible); attributes.Reset(); @@ -899,7 +898,7 @@ TEST_F(BrowserAccessibilityTest, TestValueAttributeInTextControls) { combo_box.role = ui::AX_ROLE_COMBO_BOX; combo_box_text.role = ui::AX_ROLE_STATIC_TEXT; combo_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); combo_box_text.state = 1 << ui::AX_STATE_EDITABLE; combo_box.child_ids.push_back(combo_box_text.id); @@ -914,8 +913,7 @@ TEST_F(BrowserAccessibilityTest, TestValueAttributeInTextControls) { search_box_text.role = ui::AX_ROLE_STATIC_TEXT; new_line.role = ui::AX_ROLE_LINE_BREAK; search_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | - (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); search_box_text.state = new_line.state = 1 << ui::AX_STATE_EDITABLE; search_box.child_ids.push_back(search_box_text.id); search_box.child_ids.push_back(new_line.id); @@ -943,7 +941,7 @@ TEST_F(BrowserAccessibilityTest, TestValueAttributeInTextControls) { slider_text.SetName("Slider text"); slider.role = ui::AX_ROLE_SLIDER; slider_text.role = ui::AX_ROLE_STATIC_TEXT; - slider_text.state = 1 << ui::AX_STATE_READ_ONLY; + slider.state = slider_text.state = 1 << ui::AX_STATE_READ_ONLY; slider.child_ids.push_back(slider_text.id); root.child_ids.push_back(2); // Combo box. @@ -963,27 +961,27 @@ TEST_F(BrowserAccessibilityTest, TestValueAttributeInTextControls) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(5U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* combo_box_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, combo_box_accessible); - manager->SetFocus(combo_box_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(combo_box_accessible); ASSERT_EQ(combo_box_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + ToBrowserAccessibilityWin(manager->GetFocus())); BrowserAccessibilityWin* search_box_accessible = - root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, search_box_accessible); BrowserAccessibilityWin* text_field_accessible = - root_accessible->PlatformGetChild(2)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(2)); ASSERT_NE(nullptr, text_field_accessible); BrowserAccessibilityWin* link_accessible = - root_accessible->PlatformGetChild(3)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(3)); ASSERT_NE(nullptr, link_accessible); BrowserAccessibilityWin* slider_accessible = - root_accessible->PlatformGetChild(4)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(4)); ASSERT_NE(nullptr, slider_accessible); base::win::ScopedVariant childid_self(CHILDID_SELF); @@ -1125,15 +1123,15 @@ TEST_F(BrowserAccessibilityTest, TestWordBoundariesInTextControls) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(2U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* textarea_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, textarea_accessible); BrowserAccessibilityWin* text_field_accessible = - root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, text_field_accessible); base::win::ScopedComPtr textarea_object; @@ -1196,7 +1194,7 @@ TEST_F(BrowserAccessibilityTest, TestCaretAndSelectionInSimpleFields) { combo_box.id = 2; combo_box.role = ui::AX_ROLE_COMBO_BOX; combo_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); combo_box.SetValue("Test1"); // Place the caret between 't' and 'e'. combo_box.AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, 1); @@ -1224,18 +1222,18 @@ TEST_F(BrowserAccessibilityTest, TestCaretAndSelectionInSimpleFields) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); - ASSERT_NE(nullptr, root_accessible); - ASSERT_EQ(2U, root_accessible->PlatformChildCount()); + ToBrowserAccessibilityWin(manager->GetRoot()); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(2U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* combo_box_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, combo_box_accessible); - manager->SetFocus(combo_box_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(combo_box_accessible); ASSERT_EQ(combo_box_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + ToBrowserAccessibilityWin(manager->GetFocus())); BrowserAccessibilityWin* text_field_accessible = - root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, text_field_accessible); // -2 is never a valid offset. @@ -1254,11 +1252,9 @@ TEST_F(BrowserAccessibilityTest, TestCaretAndSelectionInSimpleFields) { EXPECT_EQ(2L, caret_offset); // Move the focus to the text field. - combo_box.state &= ~(1 << ui::AX_STATE_FOCUSED); - text_field.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(text_field_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(text_field_accessible); ASSERT_EQ(text_field_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + ToBrowserAccessibilityWin(manager->GetFocus())); // The caret should not have moved. hr = text_field_accessible->get_caretOffset(&caret_offset); @@ -1346,12 +1342,12 @@ TEST_F(BrowserAccessibilityTest, TestCaretInContentEditables) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); - ASSERT_NE(nullptr, root_accessible); - ASSERT_EQ(1U, root_accessible->PlatformChildCount()); + ToBrowserAccessibilityWin(manager->GetRoot()); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(1U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* div_editable_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, div_editable_accessible); ASSERT_EQ(2U, div_editable_accessible->PlatformChildCount()); @@ -1370,21 +1366,20 @@ TEST_F(BrowserAccessibilityTest, TestCaretInContentEditables) { EXPECT_EQ(6L, caret_offset); // Move the focus to the content editable. - div_editable.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(div_editable_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(div_editable_accessible); ASSERT_EQ(div_editable_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + ToBrowserAccessibilityWin(manager->GetFocus())); BrowserAccessibilityWin* text_accessible = - div_editable_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, text_accessible); BrowserAccessibilityWin* link_accessible = - div_editable_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, link_accessible); ASSERT_EQ(1U, link_accessible->PlatformChildCount()); BrowserAccessibilityWin* link_text_accessible = - link_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(link_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, link_text_accessible); // The caret should not have moved. @@ -1465,12 +1460,12 @@ TEST_F(BrowserAccessibilityTest, TestSelectionInContentEditables) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); - ASSERT_NE(nullptr, root_accessible); - ASSERT_EQ(1U, root_accessible->PlatformChildCount()); + ToBrowserAccessibilityWin(manager->GetRoot()); + ASSERT_NE(nullptr, root_accessible); + ASSERT_EQ(1U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* div_editable_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, div_editable_accessible); ASSERT_EQ(2U, div_editable_accessible->PlatformChildCount()); @@ -1481,15 +1476,15 @@ TEST_F(BrowserAccessibilityTest, TestSelectionInContentEditables) { LONG selection_end = -2; BrowserAccessibilityWin* text_accessible = - div_editable_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, text_accessible); BrowserAccessibilityWin* link_accessible = - div_editable_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_editable_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, link_accessible); ASSERT_EQ(1U, link_accessible->PlatformChildCount()); BrowserAccessibilityWin* link_text_accessible = - link_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(link_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, link_text_accessible); // get_nSelections should work on all objects. @@ -1537,10 +1532,9 @@ TEST_F(BrowserAccessibilityTest, TestSelectionInContentEditables) { EXPECT_EQ(7L, caret_offset); // Move the focus to the content editable. - div_editable.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(div_editable_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(div_editable_accessible); ASSERT_EQ(div_editable_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + ToBrowserAccessibilityWin(manager->GetFocus())); // The caret should not have moved. hr = div_editable_accessible->get_caretOffset(&caret_offset); @@ -1602,20 +1596,20 @@ TEST_F(BrowserAccessibilityTest, TestIAccessibleHyperlink) { ASSERT_NE(nullptr, manager->GetRoot()); BrowserAccessibilityWin* root_accessible = - manager->GetRoot()->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(manager->GetRoot()); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(1U, root_accessible->PlatformChildCount()); BrowserAccessibilityWin* div_accessible = - root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, div_accessible); ASSERT_EQ(2U, div_accessible->PlatformChildCount()); BrowserAccessibilityWin* text_accessible = - div_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_accessible->PlatformGetChild(0)); ASSERT_NE(nullptr, text_accessible); BrowserAccessibilityWin* link_accessible = - div_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); + ToBrowserAccessibilityWin(div_accessible->PlatformGetChild(1)); ASSERT_NE(nullptr, link_accessible); // -1 is never a valid value. @@ -1720,25 +1714,29 @@ TEST_F(BrowserAccessibilityTest, TestIAccessibleHyperlink) { EXPECT_EQ(7, end_index); } -TEST_F(BrowserAccessibilityTest, TestPlatformDeepestFirstLastChild) { +TEST_F(BrowserAccessibilityTest, TestDeepestFirstLastChild) { ui::AXNodeData root; root.id = 1; root.role = ui::AX_ROLE_ROOT_WEB_AREA; ui::AXNodeData child1; child1.id = 2; + child1.role = ui::AX_ROLE_STATIC_TEXT; root.child_ids.push_back(2); ui::AXNodeData child2; child2.id = 3; + child2.role = ui::AX_ROLE_STATIC_TEXT; root.child_ids.push_back(3); ui::AXNodeData child2_child1; child2_child1.id = 4; + child2_child1.role = ui::AX_ROLE_INLINE_TEXT_BOX; child2.child_ids.push_back(4); ui::AXNodeData child2_child2; child2_child2.id = 5; + child2_child2.role = ui::AX_ROLE_INLINE_TEXT_BOX; child2.child_ids.push_back(5); scoped_ptr manager( @@ -1746,32 +1744,168 @@ TEST_F(BrowserAccessibilityTest, TestPlatformDeepestFirstLastChild) { MakeAXTreeUpdate(root, child1, child2, child2_child1, child2_child2), nullptr, new CountedBrowserAccessibilityFactory())); - auto root_accessible = manager->GetRoot(); + BrowserAccessibility* root_accessible = manager->GetRoot(); ASSERT_NE(nullptr, root_accessible); ASSERT_EQ(2U, root_accessible->PlatformChildCount()); - auto child1_accessible = root_accessible->PlatformGetChild(0); + BrowserAccessibility* child1_accessible = + root_accessible->PlatformGetChild(0); ASSERT_NE(nullptr, child1_accessible); - auto child2_accessible = root_accessible->PlatformGetChild(1); + BrowserAccessibility* child2_accessible = + root_accessible->PlatformGetChild(1); ASSERT_NE(nullptr, child2_accessible); - ASSERT_EQ(2U, child2_accessible->PlatformChildCount()); - auto child2_child1_accessible = child2_accessible->PlatformGetChild(0); + ASSERT_EQ(0U, child2_accessible->PlatformChildCount()); + ASSERT_EQ(2U, child2_accessible->InternalChildCount()); + BrowserAccessibility* child2_child1_accessible = + child2_accessible->InternalGetChild(0); ASSERT_NE(nullptr, child2_child1_accessible); - auto child2_child2_accessible = child2_accessible->PlatformGetChild(1); + BrowserAccessibility* child2_child2_accessible = + child2_accessible->InternalGetChild(1); ASSERT_NE(nullptr, child2_child2_accessible); EXPECT_EQ(child1_accessible, root_accessible->PlatformDeepestFirstChild()); + EXPECT_EQ(child1_accessible, root_accessible->InternalDeepestFirstChild()); + + EXPECT_EQ(child2_accessible, root_accessible->PlatformDeepestLastChild()); EXPECT_EQ(child2_child2_accessible, - root_accessible->PlatformDeepestLastChild()); + root_accessible->InternalDeepestLastChild()); + EXPECT_EQ(nullptr, child1_accessible->PlatformDeepestFirstChild()); + EXPECT_EQ(nullptr, child1_accessible->InternalDeepestFirstChild()); + EXPECT_EQ(nullptr, child1_accessible->PlatformDeepestLastChild()); + EXPECT_EQ(nullptr, child1_accessible->InternalDeepestLastChild()); + + EXPECT_EQ(nullptr, child2_accessible->PlatformDeepestFirstChild()); EXPECT_EQ(child2_child1_accessible, - child2_accessible->PlatformDeepestFirstChild()); + child2_accessible->InternalDeepestFirstChild()); + + EXPECT_EQ(nullptr, child2_accessible->PlatformDeepestLastChild()); EXPECT_EQ(child2_child2_accessible, - child2_accessible->PlatformDeepestLastChild()); + child2_accessible->InternalDeepestLastChild()); + EXPECT_EQ(nullptr, child2_child1_accessible->PlatformDeepestFirstChild()); + EXPECT_EQ(nullptr, child2_child1_accessible->InternalDeepestFirstChild()); EXPECT_EQ(nullptr, child2_child1_accessible->PlatformDeepestLastChild()); + EXPECT_EQ(nullptr, child2_child1_accessible->InternalDeepestLastChild()); EXPECT_EQ(nullptr, child2_child2_accessible->PlatformDeepestFirstChild()); + EXPECT_EQ(nullptr, child2_child2_accessible->InternalDeepestFirstChild()); EXPECT_EQ(nullptr, child2_child2_accessible->PlatformDeepestLastChild()); + EXPECT_EQ(nullptr, child2_child2_accessible->InternalDeepestLastChild()); +} + +TEST_F(BrowserAccessibilityTest, TestInheritedStringAttributes) { + ui::AXNodeData root; + root.id = 1; + root.role = ui::AX_ROLE_ROOT_WEB_AREA; + root.AddStringAttribute(ui::AX_ATTR_LANGUAGE, "en-US"); + root.AddStringAttribute(ui::AX_ATTR_FONT_FAMILY, "Helvetica"); + + ui::AXNodeData child1; + child1.id = 2; + child1.role = ui::AX_ROLE_STATIC_TEXT; + root.child_ids.push_back(2); + + ui::AXNodeData child2; + child2.id = 3; + child2.role = ui::AX_ROLE_STATIC_TEXT; + child2.AddStringAttribute(ui::AX_ATTR_LANGUAGE, "fr"); + child2.AddStringAttribute(ui::AX_ATTR_FONT_FAMILY, "Arial"); + root.child_ids.push_back(3); + + ui::AXNodeData child2_child1; + child2_child1.id = 4; + child2_child1.role = ui::AX_ROLE_INLINE_TEXT_BOX; + child2.child_ids.push_back(4); + + ui::AXNodeData child2_child2; + child2_child2.id = 5; + child2_child2.role = ui::AX_ROLE_INLINE_TEXT_BOX; + child2.child_ids.push_back(5); + + scoped_ptr manager( + BrowserAccessibilityManager::Create( + MakeAXTreeUpdate(root, child1, child2, child2_child1, child2_child2), + nullptr, new CountedBrowserAccessibilityFactory())); + + BrowserAccessibility* root_accessible = manager->GetRoot(); + ASSERT_NE(nullptr, root_accessible); + BrowserAccessibility* child1_accessible = + root_accessible->PlatformGetChild(0); + ASSERT_NE(nullptr, child1_accessible); + BrowserAccessibility* child2_accessible = + root_accessible->PlatformGetChild(1); + ASSERT_NE(nullptr, child2_accessible); + BrowserAccessibility* child2_child1_accessible = + child2_accessible->InternalGetChild(0); + ASSERT_NE(nullptr, child2_child1_accessible); + BrowserAccessibility* child2_child2_accessible = + child2_accessible->InternalGetChild(1); + ASSERT_NE(nullptr, child2_child2_accessible); + + // Test GetInheritedString16Attribute(attribute). + EXPECT_EQ( + base::UTF8ToUTF16("en-US"), + root_accessible->GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); + EXPECT_EQ( + base::UTF8ToUTF16("en-US"), + child1_accessible->GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); + EXPECT_EQ( + base::UTF8ToUTF16("fr"), + child2_accessible->GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); + EXPECT_EQ(base::UTF8ToUTF16("fr"), + child2_child1_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE)); + EXPECT_EQ(base::UTF8ToUTF16("fr"), + child2_child2_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE)); + + // Test GetInheritedString16Attribute(attribute, out_value). + base::string16 value16; + EXPECT_TRUE(root_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE, &value16)); + EXPECT_EQ(base::UTF8ToUTF16("en-US"), value16); + EXPECT_TRUE(child1_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE, &value16)); + EXPECT_EQ(base::UTF8ToUTF16("en-US"), value16); + EXPECT_TRUE(child2_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE, &value16)); + EXPECT_EQ(base::UTF8ToUTF16("fr"), value16); + EXPECT_TRUE(child2_child1_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE, &value16)); + EXPECT_EQ(base::UTF8ToUTF16("fr"), value16); + EXPECT_TRUE(child2_child2_accessible->GetInheritedString16Attribute( + ui::AX_ATTR_LANGUAGE, &value16)); + EXPECT_EQ(base::UTF8ToUTF16("fr"), value16); + + // Test GetInheritedStringAttribute(attribute). + EXPECT_EQ("Helvetica", root_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY)); + EXPECT_EQ("Helvetica", child1_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY)); + EXPECT_EQ("Arial", child2_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY)); + EXPECT_EQ("Arial", child2_child1_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY)); + EXPECT_EQ("Arial", child2_child2_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY)); + + // Test GetInheritedStringAttribute(attribute, out_value). + std::string value; + EXPECT_TRUE(root_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY, &value)); + EXPECT_EQ("Helvetica", value); + EXPECT_TRUE(child1_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY, &value)); + EXPECT_EQ("Helvetica", value); + EXPECT_TRUE(child2_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY, &value)); + EXPECT_EQ("Arial", value); + EXPECT_TRUE(child2_child1_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY, &value)); + EXPECT_EQ("Arial", value); + EXPECT_TRUE(child2_child2_accessible->GetInheritedStringAttribute( + ui::AX_ATTR_FONT_FAMILY, &value)); + EXPECT_EQ("Arial", value); } TEST_F(BrowserAccessibilityTest, TestSanitizeStringAttributeForIA2) { @@ -1797,9 +1931,9 @@ TEST_F(BrowserAccessibilityTest, UniqueIdWinInvalidAfterDeletingTree) { new CountedBrowserAccessibilityFactory())); BrowserAccessibility* root = manager->GetRoot(); - LONG root_unique_id = root->ToBrowserAccessibilityWin()->unique_id_win(); + int32_t root_unique_id = root->unique_id(); BrowserAccessibility* child = root->PlatformGetChild(0); - LONG child_unique_id = child->ToBrowserAccessibilityWin()->unique_id_win(); + int32_t child_unique_id = child->unique_id(); // Now destroy that original tree and create a new tree. manager.reset( @@ -1808,39 +1942,67 @@ TEST_F(BrowserAccessibilityTest, UniqueIdWinInvalidAfterDeletingTree) { nullptr, new CountedBrowserAccessibilityFactory())); root = manager->GetRoot(); - LONG root_unique_id_2 = root->ToBrowserAccessibilityWin()->unique_id_win(); + int32_t root_unique_id_2 = root->unique_id(); child = root->PlatformGetChild(0); - LONG child_unique_id_2 = child->ToBrowserAccessibilityWin()->unique_id_win(); + int32_t child_unique_id_2 = child->unique_id(); // The nodes in the new tree should not have the same ids. EXPECT_NE(root_unique_id, root_unique_id_2); EXPECT_NE(child_unique_id, child_unique_id_2); // Trying to access the unique IDs of the old, deleted objects should fail. - base::win::ScopedVariant old_root_variant(root_unique_id); + base::win::ScopedVariant old_root_variant(-root_unique_id); base::win::ScopedComPtr old_root_dispatch; - HRESULT hr = root->ToBrowserAccessibilityWin()->get_accChild( + HRESULT hr = ToBrowserAccessibilityWin(root)->get_accChild( old_root_variant, old_root_dispatch.Receive()); EXPECT_EQ(E_INVALIDARG, hr); - base::win::ScopedVariant old_child_variant(child_unique_id); + base::win::ScopedVariant old_child_variant(-child_unique_id); base::win::ScopedComPtr old_child_dispatch; - hr = root->ToBrowserAccessibilityWin()->get_accChild( + hr = ToBrowserAccessibilityWin(root)->get_accChild( old_child_variant, old_child_dispatch.Receive()); EXPECT_EQ(E_INVALIDARG, hr); // Trying to access the unique IDs of the new objects should succeed. - base::win::ScopedVariant new_root_variant(root_unique_id_2); + base::win::ScopedVariant new_root_variant(-root_unique_id_2); base::win::ScopedComPtr new_root_dispatch; - hr = root->ToBrowserAccessibilityWin()->get_accChild( + hr = ToBrowserAccessibilityWin(root)->get_accChild( new_root_variant, new_root_dispatch.Receive()); EXPECT_EQ(S_OK, hr); - base::win::ScopedVariant new_child_variant(child_unique_id_2); + base::win::ScopedVariant new_child_variant(-child_unique_id_2); base::win::ScopedComPtr new_child_dispatch; - hr = root->ToBrowserAccessibilityWin()->get_accChild( + hr = ToBrowserAccessibilityWin(root)->get_accChild( new_child_variant, new_child_dispatch.Receive()); EXPECT_EQ(S_OK, hr); } +TEST_F(BrowserAccessibilityTest, AccChildOnlyReturnsDescendants) { + ui::AXNodeData root_node; + root_node.id = 1; + root_node.role = ui::AX_ROLE_ROOT_WEB_AREA; + + ui::AXNodeData child_node; + child_node.id = 2; + root_node.child_ids.push_back(2); + + scoped_ptr manager( + new BrowserAccessibilityManagerWin( + MakeAXTreeUpdate(root_node, child_node), + nullptr, + new CountedBrowserAccessibilityFactory())); + + BrowserAccessibility* root = manager->GetRoot(); + BrowserAccessibility* child = root->PlatformGetChild(0); + + base::win::ScopedVariant root_unique_id_variant(-root->unique_id()); + base::win::ScopedComPtr result; + EXPECT_EQ(E_INVALIDARG, ToBrowserAccessibilityWin(child)->get_accChild( + root_unique_id_variant, result.Receive())); + + base::win::ScopedVariant child_unique_id_variant(-child->unique_id()); + EXPECT_EQ(S_OK, ToBrowserAccessibilityWin(root)->get_accChild( + child_unique_id_variant, result.Receive())); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc index b5dd3296ef3..986fdc063bd 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.cc @@ -15,21 +15,28 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/thread_task_runner_handle.h" #include "build/build_config.h" #include "content/browser/accessibility/accessibility_tree_formatter.h" #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/frame_host/render_widget_host_view_child_frame.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.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/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "content/test/accessibility_browser_test_utils.h" +#include "content/test/content_browser_test_utils_internal.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" namespace content { @@ -40,6 +47,29 @@ const char kMarkSkipFile[] = "#GetRole() == ui::AX_ROLE_WEB_AREA || + node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) && + node->GetStringAttribute(ui::AX_ATTR_URL) == url) { + // If possible, ensure the doc has finished loading. That's currently + // not possible with same-process iframes until https://crbug.com/532249 + // is fixed. + return (node->manager()->GetTreeData().url != url || + node->manager()->GetTreeData().loaded); + } + + for (unsigned i = 0; i < node->PlatformChildCount(); i++) { + if (AccessibilityTreeContainsLoadedDocWithUrl( + node->PlatformGetChild(i), url)) { + return true; + } + } + return false; +} + } // namespace typedef AccessibilityTreeFormatter::Filter Filter; @@ -50,6 +80,17 @@ DumpAccessibilityTestBase::DumpAccessibilityTestBase() { DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { } +void DumpAccessibilityTestBase::SetUpCommandLine( + base::CommandLine* command_line) { + IsolateAllSitesForTesting(command_line); +} + +void DumpAccessibilityTestBase::SetUpOnMainThread() { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + SetupCrossSiteRedirector(embedded_test_server()); +} + base::string16 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { scoped_ptr formatter( @@ -95,7 +136,7 @@ std::vector DumpAccessibilityTestBase::DiffLines( void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( const std::string& test_html, std::vector* filters, - std::string* wait_for) { + std::vector* wait_for) { for (const std::string& line : base::SplitString(test_html, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { @@ -120,7 +161,7 @@ void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( Filter::DENY)); } else if (base::StartsWith(line, wait_str, base::CompareCase::SENSITIVE)) { - *wait_for = line.substr(wait_str.size()); + wait_for->push_back(line.substr(wait_str.size())); } } } @@ -193,42 +234,109 @@ void DumpAccessibilityTestBase::RunTestForPlatform( } // Parse filters and other directives in the test file. - std::string wait_for; + std::vector wait_for; AddDefaultFilters(&filters_); ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); - // Load the page. - base::string16 html_contents16; - html_contents16 = base::UTF8ToUTF16(html_contents); - GURL url = GetTestUrl(file_dir, file_path.BaseName().MaybeAsASCII().c_str()); - - // If there's a @WAIT-FOR directive, set up an accessibility notification - // waiter that returns on any event; we'll stop when we get the text we're - // waiting for, or time out. Otherwise just wait specifically for - // the "load complete" event. - scoped_ptr waiter; - if (!wait_for.empty()) { - waiter.reset(new AccessibilityNotificationWaiter( - shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE)); - } else { - waiter.reset(new AccessibilityNotificationWaiter( - shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE)); + // Load the test html and wait for the "load complete" AX event. + GURL url(embedded_test_server()->GetURL( + "/" + std::string(file_dir) + "/" + file_path.BaseName().MaybeAsASCII())); + AccessibilityNotificationWaiter accessibility_waiter( + shell(), + AccessibilityModeComplete, + ui::AX_EVENT_LOAD_COMPLETE); + NavigateToURL(shell(), url); + accessibility_waiter.WaitForNotification(); + + // Get the url of every frame in the frame tree. + WebContentsImpl* web_contents = static_cast( + shell()->web_contents()); + FrameTree* frame_tree = web_contents->GetFrameTree(); + std::vector all_frame_urls; + for (FrameTreeNode* node : frame_tree->Nodes()) { + // Ignore about:blank urls because of the case where a parent frame A + // has a child iframe B and it writes to the document using + // contentDocument.open() on the child frame B. + // + // In this scenario, B's contentWindow.location.href matches A's url, + // but B's url in the browser frame tree is still "about:blank". + std::string url = node->current_url().spec(); + if (url != url::kAboutBlankURL) + all_frame_urls.push_back(url); + + // We won't get the correct coordinate transformations for + // out-of-process iframes until each frame's surface is ready. + RenderFrameHostImpl* current_frame_host = node->current_frame_host(); + if (!current_frame_host) + continue; + RenderWidgetHostImpl* rwh = current_frame_host->GetRenderWidgetHost(); + if (!rwh) + continue; + RenderWidgetHostViewBase* rwhv = rwh->GetView(); + if (rwhv && rwhv->IsChildFrameForTesting()) { + SurfaceHitTestReadyNotifier notifier( + static_cast(rwhv)); + notifier.WaitForSurfaceReady(); + } } - // Load the test html. - NavigateToURL(shell(), url); + // Wait for the accessibility tree to fully load for all frames, + // by searching for the WEB_AREA node in the accessibility tree + // with the url of each frame in our frame tree. Note that this + // doesn't support cases where there are two iframes with the + // exact same url. If all frames haven't loaded yet, set up a + // listener for accessibility events on any frame and block + // until the next one is received. + // + // If the original page has a @WAIT-FOR directive, don't break until + // the text we're waiting for appears in the full text dump of the + // accessibility tree, either. + for (;;) { + VLOG(1) << "Top of loop"; + RenderFrameHostImpl* main_frame = static_cast( + web_contents->GetMainFrame()); + BrowserAccessibilityManager* manager = + main_frame->browser_accessibility_manager(); + if (manager) { + BrowserAccessibility* accessibility_root = + manager->GetRoot(); + + // Check to see if all frames have loaded. + bool all_frames_loaded = true; + for (const auto& url : all_frame_urls) { + if (!AccessibilityTreeContainsLoadedDocWithUrl( + accessibility_root, url)) { + VLOG(1) << "Still waiting on this frame to load: " << url; + all_frames_loaded = false; + break; + } + } - // Wait for notifications. If there's a @WAIT-FOR directive, break when - // the text we're waiting for appears in the dump, otherwise break after - // the first notification, which will be a load complete. - do { - waiter->WaitForNotification(); - if (!wait_for.empty()) { + // Check to see if the @WAIT-FOR text has appeared yet. + bool all_wait_for_strings_found = true; base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); - if (base::UTF16ToUTF8(tree_dump).find(wait_for) != std::string::npos) - wait_for.clear(); + for (const auto& str : wait_for) { + if (base::UTF16ToUTF8(tree_dump).find(str) == std::string::npos) { + VLOG(1) << "Still waiting on this text to be found: " << str; + all_wait_for_strings_found = false; + break; + } + } + + // If all frames have loaded and the @WAIT-FOR text has appeared, + // we're done. + if (all_frames_loaded && all_wait_for_strings_found) + break; } - } while (!wait_for.empty()); + + // Block until the next accessibility notification in any frame. + VLOG(1) << "Waiting until the next accessibility event"; + AccessibilityNotificationWaiter accessibility_waiter(main_frame, + ui::AX_EVENT_NONE); + for (FrameTreeNode* node : frame_tree->Nodes()) + accessibility_waiter.ListenToAdditionalFrame(node->current_frame_host()); + accessibility_waiter.WaitForNotification(); + } // Call the subclass to dump the output. std::vector actual_lines = Dump(); diff --git a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.h b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.h index 21ae32a1faf..5e93b9be562 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.h +++ b/chromium/content/browser/accessibility/dump_accessibility_browsertest_base.h @@ -31,6 +31,9 @@ class DumpAccessibilityTestBase : public ContentBrowserTest { void RunTest(const base::FilePath file_path, const char* file_dir); protected: + void SetUpCommandLine(base::CommandLine* command_line) override; + void SetUpOnMainThread() override; + // // For subclasses to override: // @@ -79,11 +82,12 @@ class DumpAccessibilityTestBase : public ContentBrowserTest { // until the given string (e.g., "text") appears in the resulting dump. // A test can make some changes to the document, then append a magic string // indicating that the test is done, and this framework will wait for that - // string to appear before comparing the results. + // string to appear before comparing the results. There can be multiple + // @WAIT-FOR: directives. void ParseHtmlForExtraDirectives( const std::string& test_html, std::vector* filters, - std::string* wait_for); + std::vector* wait_for); // Create the right AccessibilityTreeFormatter subclass. AccessibilityTreeFormatter* CreateAccessibilityTreeFormatter(); diff --git a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc index e4d2fc46dd6..40d33366e90 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc @@ -202,8 +202,16 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, RunEventTest(FILE_PATH_LITERAL("inner-html-change.html")); } +#if defined(OS_MACOSX) +// Mac failures: http://crbug.com/598527. +#define MAYBE_AccessibilityEventsInputTypeTextValueChanged \ + DISABLED_AccessibilityEventsInputTypeTextValueChanged +#else +#define MAYBE_AccessibilityEventsInputTypeTextValueChanged \ + AccessibilityEventsInputTypeTextValueChanged +#endif IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - AccessibilityEventsInputTypeTextValueChanged) { + MAYBE_AccessibilityEventsInputTypeTextValueChanged) { RunEventTest(FILE_PATH_LITERAL("input-type-text-value-changed.html")); } @@ -213,18 +221,9 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, } // Flaky on Windows: http://crbug.com/486861 -#if defined(OS_WIN) -#define MAYBE_AccessibilityEventsListboxNext \ - DISABLED_AccessibilityEventsListboxNext -#define MAYBE_AccessibilityEventsMenuListPopup \ - DISABLED_AccessibilityEventsMenuListPopup -#else -#define MAYBE_AccessibilityEventsListboxNext AccessibilityEventsListboxNext -#define MAYBE_AccessibilityEventsMenuListPopup AccessibilityEventsMenuListPopup -#endif - +// Flaky on Mac: http://crbug.com/588271 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - MAYBE_AccessibilityEventsListboxNext) { + DISABLED_AccessibilityEventsListboxNext) { RunEventTest(FILE_PATH_LITERAL("listbox-next.html")); } @@ -238,8 +237,9 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, RunEventTest(FILE_PATH_LITERAL("menulist-next.html")); } +// Flaky on Windows: http://crbug.com/486861 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - MAYBE_AccessibilityEventsMenuListPopup) { + DISABLED_AccessibilityEventsMenuListPopup) { RunEventTest(FILE_PATH_LITERAL("menulist-popup.html")); } diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 9db7c77a84c..e1aa6a44d15 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -55,12 +55,14 @@ class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { void AddDefaultFilters(std::vector* filters) override { filters->push_back(Filter(base::ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW)); filters->push_back(Filter(base::ASCIIToUTF16("READONLY"), Filter::ALLOW)); - filters->push_back(Filter(base::ASCIIToUTF16("name*"), Filter::ALLOW)); + filters->push_back(Filter(base::ASCIIToUTF16("name=*"), Filter::ALLOW)); + filters->push_back(Filter(base::ASCIIToUTF16("roleDescription=*"), + Filter::ALLOW)); filters->push_back(Filter(base::ASCIIToUTF16("*=''"), Filter::DENY)); } void SetUpCommandLine(base::CommandLine* command_line) override { - ContentBrowserTest::SetUpCommandLine(command_line); + DumpAccessibilityTestBase::SetUpCommandLine(command_line); // Enable , which is used in some tests. base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalWebPlatformFeatures); @@ -77,6 +79,17 @@ class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { RunTest(aria_file, "accessibility/aria"); } + void RunCSSTest(const base::FilePath::CharType* file_path) { + base::FilePath dir_test_data; + ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); + base::FilePath test_path( + dir_test_data.AppendASCII("accessibility").AppendASCII("css")); + ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName(); + + base::FilePath css_file = test_path.Append(base::FilePath(file_path)); + RunTest(css_file, "accessibility/css"); + } + void RunHtmlTest(const base::FilePath::CharType* file_path) { base::FilePath dir_test_data; ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); @@ -105,6 +118,18 @@ class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { } }; +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCSSColor) { + RunCSSTest(FILE_PATH_LITERAL("color.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCSSFontStyle) { + RunCSSTest(FILE_PATH_LITERAL("font-style.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCSSLanguage) { + RunCSSTest(FILE_PATH_LITERAL("language.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { RunHtmlTest(FILE_PATH_LITERAL("a.html")); } @@ -113,6 +138,10 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAbbr) { RunHtmlTest(FILE_PATH_LITERAL("abbr.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityActionVerbs) { + RunHtmlTest(FILE_PATH_LITERAL("action-verbs.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAddress) { RunHtmlTest(FILE_PATH_LITERAL("address.html")); } @@ -554,7 +583,12 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaTextbox) { } IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, - AccessibilityAriaTextboxWithSelection) { + AccessibilityAriaTextboxWithRichText) { + RunAriaTest(FILE_PATH_LITERAL("aria-textbox-with-rich-text.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityAriaTextboxWithSelection) { RunAriaTest(FILE_PATH_LITERAL("aria-textbox-with-selection.html")); } @@ -826,21 +860,59 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityIframe) { RunHtmlTest(FILE_PATH_LITERAL("iframe.html")); } -// Flaky. See http://crbug.com/224659. IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, - DISABLED_AccessibilityIframeCoordinates) { + AccessibilityIframeCrossProcess) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-cross-process.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeCoordinates) { RunHtmlTest(FILE_PATH_LITERAL("iframe-coordinates.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeCoordinatesCrossProcess) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-coordinates-cross-process.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityIframePresentational) { RunHtmlTest(FILE_PATH_LITERAL("iframe-presentational.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeTransform) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-transform.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeTransformCrossProcess) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-transform-cross-process.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeTransformNested) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-transform-nested.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeTransformNestedCrossProcess) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-transform-nested-cross-process.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityIframeTransformScrolled) { + RunHtmlTest(FILE_PATH_LITERAL("iframe-transform-scrolled.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityImg) { RunHtmlTest(FILE_PATH_LITERAL("img.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityImgEmptyAlt) { + RunHtmlTest(FILE_PATH_LITERAL("img-empty-alt.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputButton) { RunHtmlTest(FILE_PATH_LITERAL("input-button.html")); } @@ -1074,13 +1146,29 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, RunHtmlTest(FILE_PATH_LITERAL("modal-dialog-opened.html")); } +// Flaky on Windows and Mac: crbug.com/593846 +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_AccessibilityModalDialogInIframeClosed \ + DISABLED_AccessibilityModalDialogInIframeClosed +#else +#define MAYBE_AccessibilityModalDialogInIframeClosed \ + AccessibilityModalDialogInIframeClosed +#endif IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, - AccessibilityModalDialogInIframeClosed) { + MAYBE_AccessibilityModalDialogInIframeClosed) { RunHtmlTest(FILE_PATH_LITERAL("modal-dialog-in-iframe-closed.html")); } +// Flaky on Windows and Mac: crbug.com/593846 +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_AccessibilityModalDialogInIframeOpened \ + DISABLED_AccessibilityModalDialogInIframeOpened +#else +#define MAYBE_AccessibilityModalDialogInIframeOpened \ + AccessibilityModalDialogInIframeOpened +#endif IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, - AccessibilityModalDialogInIframeOpened) { + MAYBE_AccessibilityModalDialogInIframeOpened) { RunHtmlTest(FILE_PATH_LITERAL("modal-dialog-in-iframe-opened.html")); } diff --git a/chromium/content/browser/accessibility/hit_testing_browsertest.cc b/chromium/content/browser/accessibility/hit_testing_browsertest.cc new file mode 100644 index 00000000000..7610f73da1d --- /dev/null +++ b/chromium/content/browser/accessibility/hit_testing_browsertest.cc @@ -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. + +#include +#include +#include + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/browser/accessibility/accessibility_tree_formatter.h" +#include "content/browser/accessibility/browser_accessibility.h" +#include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_paths.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/accessibility_browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +bool AXTreeContainsNodeWithName(BrowserAccessibility* node, + const std::string& name) { + if (node->GetStringAttribute(ui::AX_ATTR_NAME) == name) + return true; + + for (unsigned i = 0; i < node->PlatformChildCount(); i++) { + if (AXTreeContainsNodeWithName(node->PlatformGetChild(i), name)) + return true; + } + + return false; +} + +} // namespace + +class AccessibilityHitTestingBrowserTest : public ContentBrowserTest { + public: + AccessibilityHitTestingBrowserTest() {} + ~AccessibilityHitTestingBrowserTest() override {} + + protected: + BrowserAccessibility* HitTestAndWaitForResult(const gfx::Point& point) { + WebContentsImpl* web_contents = + static_cast(shell()->web_contents()); + FrameTree* frame_tree = web_contents->GetFrameTree(); + BrowserAccessibilityManager* manager = + web_contents->GetRootBrowserAccessibilityManager(); + + AccessibilityNotificationWaiter hover_waiter( + shell(), AccessibilityModeComplete, ui::AX_EVENT_HOVER); + for (FrameTreeNode* node : frame_tree->Nodes()) + hover_waiter.ListenToAdditionalFrame(node->current_frame_host()); + manager->delegate()->AccessibilityHitTest(point); + hover_waiter.WaitForNotification(); + + RenderFrameHostImpl* target_frame = hover_waiter.event_render_frame_host(); + BrowserAccessibilityManager* target_manager = + target_frame->browser_accessibility_manager(); + int hover_target_id = hover_waiter.event_target_id(); + BrowserAccessibility* hovered_node = + target_manager->GetFromID(hover_target_id); + return hovered_node; + } +}; + +IN_PROC_BROWSER_TEST_F(AccessibilityHitTestingBrowserTest, + HitTestOutsideDocumentBoundsReturnsRoot) { + NavigateToURL(shell(), GURL(url::kAboutBlankURL)); + + // Load the page. + AccessibilityNotificationWaiter waiter(shell(), AccessibilityModeComplete, + ui::AX_EVENT_LOAD_COMPLETE); + const char url_str[] = + "data:text/html," + "" + "Accessibility Test" + "" + "" + "This is some text in a link" + "" + ""; + GURL url(url_str); + NavigateToURL(shell(), url); + waiter.WaitForNotification(); + + BrowserAccessibility* hovered_node = + HitTestAndWaitForResult(gfx::Point(-1, -1)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, hovered_node->GetRole()); +} + +IN_PROC_BROWSER_TEST_F(AccessibilityHitTestingBrowserTest, + HitTestingInIframes) { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + + NavigateToURL(shell(), GURL(url::kAboutBlankURL)); + + AccessibilityNotificationWaiter waiter(shell(), AccessibilityModeComplete, + ui::AX_EVENT_LOAD_COMPLETE); + GURL url(embedded_test_server()->GetURL( + "/accessibility/html/iframe-coordinates.html")); + NavigateToURL(shell(), url); + waiter.WaitForNotification(); + + WebContentsImpl* web_contents = + static_cast(shell()->web_contents()); + FrameTree* frame_tree = web_contents->GetFrameTree(); + BrowserAccessibilityManager* manager = + web_contents->GetRootBrowserAccessibilityManager(); + BrowserAccessibility* root = manager->GetRoot(); + while (!AXTreeContainsNodeWithName(root, "Ordinary Button") || + !AXTreeContainsNodeWithName(root, "Scrolled Button")) { + AccessibilityNotificationWaiter waiter(shell(), AccessibilityModeComplete, + ui::AX_EVENT_NONE); + for (FrameTreeNode* node : frame_tree->Nodes()) + waiter.ListenToAdditionalFrame(node->current_frame_host()); + waiter.WaitForNotification(); + } + + // Send a series of hit test requests, and for each one + // wait for the hover event in response, verifying we hit the + // correct object. + + // (50, 50) -> "Button" + BrowserAccessibility* hovered_node; + hovered_node = HitTestAndWaitForResult(gfx::Point(50, 50)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_BUTTON, hovered_node->GetRole()); + ASSERT_EQ("Button", hovered_node->GetStringAttribute(ui::AX_ATTR_NAME)); + + // (50, 305) -> div in first iframe + hovered_node = HitTestAndWaitForResult(gfx::Point(50, 305)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_DIV, hovered_node->GetRole()); + + // (50, 350) -> "Ordinary Button" + hovered_node = HitTestAndWaitForResult(gfx::Point(50, 350)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_BUTTON, hovered_node->GetRole()); + ASSERT_EQ("Ordinary Button", + hovered_node->GetStringAttribute(ui::AX_ATTR_NAME)); + + // (50, 455) -> "Scrolled Button" + hovered_node = HitTestAndWaitForResult(gfx::Point(50, 455)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_BUTTON, hovered_node->GetRole()); + ASSERT_EQ("Scrolled Button", + hovered_node->GetStringAttribute(ui::AX_ATTR_NAME)); + + // (50, 505) -> div in second iframe + hovered_node = HitTestAndWaitForResult(gfx::Point(50, 505)); + ASSERT_TRUE(hovered_node != NULL); + ASSERT_EQ(ui::AX_ROLE_DIV, hovered_node->GetRole()); +} + +} // namespace content diff --git a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc index 8b17f2d9fd2..1a0239eb58b 100644 --- a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc +++ b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc @@ -11,6 +11,7 @@ #include "base/strings/utf_string_conversions.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "ui/accessibility/ax_enums.h" namespace content { @@ -210,4 +211,249 @@ bool OneShotAccessibilityTreeSearch::Matches(BrowserAccessibility* node) { return true; } +// +// Predicates +// + +bool AccessibilityArticlePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return node->GetRole() == ui::AX_ROLE_ARTICLE; +} + +bool AccessibilityButtonPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + switch (node->GetRole()) { + case ui::AX_ROLE_BUTTON: + case ui::AX_ROLE_MENU_BUTTON: + case ui::AX_ROLE_POP_UP_BUTTON: + case ui::AX_ROLE_SWITCH: + case ui::AX_ROLE_TOGGLE_BUTTON: + return true; + default: + return false; + } +} + +bool AccessibilityBlockquotePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return node->GetRole() == ui::AX_ROLE_BLOCKQUOTE; +} + +bool AccessibilityCheckboxPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_CHECK_BOX || + node->GetRole() == ui::AX_ROLE_MENU_ITEM_CHECK_BOX); +} + +bool AccessibilityComboboxPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_COMBO_BOX || + node->GetRole() == ui::AX_ROLE_POP_UP_BUTTON); +} + +bool AccessibilityControlPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + if (node->IsControl()) + return true; + if (node->HasState(ui::AX_STATE_FOCUSABLE) && + node->GetRole() != ui::AX_ROLE_IFRAME && + node->GetRole() != ui::AX_ROLE_IFRAME_PRESENTATIONAL && + node->GetRole() != ui::AX_ROLE_IMAGE_MAP_LINK && + node->GetRole() != ui::AX_ROLE_LINK && + node->GetRole() != ui::AX_ROLE_WEB_AREA && + node->GetRole() != ui::AX_ROLE_ROOT_WEB_AREA) { + return true; + } + return false; +} + +bool AccessibilityFocusablePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + bool focusable = node->HasState(ui::AX_STATE_FOCUSABLE); + if (node->GetRole() == ui::AX_ROLE_IFRAME || + node->GetRole() == ui::AX_ROLE_IFRAME_PRESENTATIONAL || + node->GetRole() == ui::AX_ROLE_WEB_AREA || + node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA) { + focusable = false; + } + return focusable; +} + +bool AccessibilityGraphicPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return node->GetRole() == ui::AX_ROLE_IMAGE; +} + +bool AccessibilityHeadingPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING); +} + +bool AccessibilityH1Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 1); +} + +bool AccessibilityH2Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 2); +} + +bool AccessibilityH3Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 3); +} + +bool AccessibilityH4Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 4); +} + +bool AccessibilityH5Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 5); +} + +bool AccessibilityH6Predicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 6); +} + +bool AccessibilityHeadingSameLevelPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_HEADING && + start->GetRole() == ui::AX_ROLE_HEADING && + (node->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == + start->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL))); +} + +bool AccessibilityFramePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + if (node->IsWebAreaForPresentationalIframe()) + return false; + if (!node->GetParent()) + return false; + return (node->GetRole() == ui::AX_ROLE_WEB_AREA || + node->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA); +} + +bool AccessibilityLandmarkPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + switch (node->GetRole()) { + case ui::AX_ROLE_APPLICATION: + case ui::AX_ROLE_ARTICLE: + case ui::AX_ROLE_BANNER: + case ui::AX_ROLE_COMPLEMENTARY: + case ui::AX_ROLE_CONTENT_INFO: + case ui::AX_ROLE_MAIN: + case ui::AX_ROLE_NAVIGATION: + case ui::AX_ROLE_SEARCH: + case ui::AX_ROLE_REGION: + return true; + default: + return false; + } +} + +bool AccessibilityLinkPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_LINK || + node->GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK); +} + +bool AccessibilityListPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_LIST_BOX || + node->GetRole() == ui::AX_ROLE_LIST || + node->GetRole() == ui::AX_ROLE_DESCRIPTION_LIST); +} + +bool AccessibilityListItemPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_LIST_ITEM || + node->GetRole() == ui::AX_ROLE_DESCRIPTION_LIST_TERM || + node->GetRole() == ui::AX_ROLE_LIST_BOX_OPTION); +} + +bool AccessibilityLiveRegionPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return node->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS); +} + +bool AccessibilityMainPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_MAIN); +} + +bool AccessibilityMediaPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + const std::string& tag = node->GetStringAttribute(ui::AX_ATTR_HTML_TAG); + return tag == "audio" || tag == "video"; +} + +bool AccessibilityRadioButtonPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_RADIO_BUTTON || + node->GetRole() == ui::AX_ROLE_MENU_ITEM_RADIO); +} + +bool AccessibilityRadioGroupPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return node->GetRole() == ui::AX_ROLE_RADIO_GROUP; +} + +bool AccessibilityTablePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->GetRole() == ui::AX_ROLE_TABLE || + node->GetRole() == ui::AX_ROLE_GRID); +} + +bool AccessibilityTextfieldPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->IsSimpleTextControl() || node->IsRichTextControl()); +} + +bool AccessibilityTextStyleBoldPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + int32_t style = node->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); + return 0 != (style & ui::AX_TEXT_STYLE_BOLD); +} + +bool AccessibilityTextStyleItalicPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + int32_t style = node->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); + return 0 != (style & ui::AX_TEXT_STYLE_BOLD); +} + +bool AccessibilityTextStyleUnderlinePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + int32_t style = node->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); + return 0 != (style & ui::AX_TEXT_STYLE_UNDERLINE); +} + +bool AccessibilityTreePredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return (node->IsSimpleTextControl() || node->IsRichTextControl()); +} + +bool AccessibilityUnvisitedLinkPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return ((node->GetRole() == ui::AX_ROLE_LINK || + node->GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK) && + !node->HasState(ui::AX_STATE_VISITED)); +} + +bool AccessibilityVisitedLinkPredicate( + BrowserAccessibility* start, BrowserAccessibility* node) { + return ((node->GetRole() == ui::AX_ROLE_LINK || + node->GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK) && + node->HasState(ui::AX_STATE_VISITED)); +} + } // namespace content diff --git a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.h b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.h index 2538bb8aee0..df79eb94e02 100644 --- a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.h +++ b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.h @@ -24,6 +24,47 @@ typedef bool (*AccessibilityMatchPredicate)( BrowserAccessibility* start_element, BrowserAccessibility* this_element); +#define DECLARE_ACCESSIBILITY_PREDICATE(PredicateName) \ + bool PredicateName(BrowserAccessibility* start_element, \ + BrowserAccessibility* this_element); + +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityArticlePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityBlockquotePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityButtonPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityCheckboxPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityComboboxPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityControlPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityFocusablePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityGraphicPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityHeadingPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH1Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH2Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH3Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH4Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH5Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityH6Predicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityHeadingSameLevelPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityFramePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityLandmarkPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityLinkPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityListPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityListItemPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityLiveRegionPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityMainPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityMediaPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityRadioButtonPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityRadioGroupPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTablePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTextfieldPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTextStyleBoldPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTextStyleItalicPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTextStyleUnderlinePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTreePredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityUnvisitedLinkPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityVisitedLinkPredicate); +DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTextStyleBoldPredicate); + + // This class provides an interface for searching the accessibility tree from // a given starting node, with a few built-in options and allowing an arbitrary // number of predicates that can be used to restrict the search. diff --git a/chromium/content/browser/accessibility/site_per_process_accessibility_browsertest.cc b/chromium/content/browser/accessibility/site_per_process_accessibility_browsertest.cc index 37107702569..75833ff9aa8 100644 --- a/chromium/content/browser/accessibility/site_per_process_accessibility_browsertest.cc +++ b/chromium/content/browser/accessibility/site_per_process_accessibility_browsertest.cc @@ -58,15 +58,6 @@ bool AccessibilityTreeContainsText(BrowserAccessibility* node, return false; } -// Helper function to be used with FrameTree::ForEach, so that -// AccessibilityNotificationWaiter can listen for accessibility -// events in all frames. -bool ListenToFrame(AccessibilityNotificationWaiter* waiter, - FrameTreeNode* frame_tree_node) { - waiter->ListenToAdditionalFrame(frame_tree_node->current_frame_host()); - return true; -} - } // namespace class MAYBE_SitePerProcessAccessibilityBrowserTest @@ -113,7 +104,10 @@ class MAYBE_SitePerProcessAccessibilityBrowserTest main_frame_manager->GetRoot(), text)) { AccessibilityNotificationWaiter accessibility_waiter(main_frame, ui::AX_EVENT_NONE); - frame_tree->ForEach(base::Bind(ListenToFrame, &accessibility_waiter)); + for (FrameTreeNode* node : frame_tree->Nodes()) + accessibility_waiter.ListenToAdditionalFrame( + node->current_frame_host()); + accessibility_waiter.WaitForNotification(); } } diff --git a/chromium/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/chromium/content/browser/accessibility/snapshot_ax_tree_browsertest.cc index da6201f5b6b..7dc177d8e5d 100644 --- a/chromium/content/browser/accessibility/snapshot_ax_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/snapshot_ax_tree_browsertest.cc @@ -9,6 +9,9 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" +#include "content/test/content_browser_test_utils_internal.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_tree.h" @@ -39,6 +42,19 @@ class AXTreeSnapshotWaiter { DISALLOW_COPY_AND_ASSIGN(AXTreeSnapshotWaiter); }; +void DumpRolesAndNamesAsText(const ui::AXNode* node, + int indent, + std::string* dst) { + for (int i = 0; i < indent; i++) + *dst += " "; + *dst += ui::ToString(node->data().role); + if (node->data().HasStringAttribute(ui::AX_ATTR_NAME)) + *dst += " '" + node->data().GetStringAttribute(ui::AX_ATTR_NAME) + "'"; + *dst += "\n"; + for (int i = 0; i < node->child_count(); ++i) + DumpRolesAndNamesAsText(node->children()[i], indent + 1, dst); +} + } // namespace class SnapshotAXTreeBrowserTest : public ContentBrowserTest { @@ -75,4 +91,54 @@ IN_PROC_BROWSER_TEST_F(SnapshotAXTreeBrowserTest, ASSERT_EQ(ui::AX_ROLE_BUTTON, button->data().role); } +IN_PROC_BROWSER_TEST_F(SnapshotAXTreeBrowserTest, + SnapshotAccessibilityTreeFromMultipleFrames) { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + + NavigateToURL(shell(), embedded_test_server()->GetURL( + "/accessibility/snapshot/outer.html")); + + WebContentsImpl* web_contents = + static_cast(shell()->web_contents()); + FrameTreeNode* root_frame = web_contents->GetFrameTree()->root(); + + NavigateFrameToURL(root_frame->child_at(0), GURL("data:text/plain,Alpha")); + NavigateFrameToURL(root_frame->child_at(1), embedded_test_server()->GetURL( + "/accessibility/snapshot/inner.html")); + + AXTreeSnapshotWaiter waiter; + web_contents->RequestAXTreeSnapshot( + base::Bind(&AXTreeSnapshotWaiter::ReceiveSnapshot, + base::Unretained(&waiter))); + waiter.Wait(); + + // Dump the whole tree if one of the assertions below fails + // to aid in debugging why it failed. + SCOPED_TRACE(waiter.snapshot().ToString()); + + ui::AXTree tree(waiter.snapshot()); + ui::AXNode* root = tree.root(); + std::string dump; + DumpRolesAndNamesAsText(root, 0, &dump); + EXPECT_EQ( + "rootWebArea\n" + " group\n" + " button 'Before'\n" + " iframe\n" + " group\n" + " pre\n" + " staticText 'Alpha'\n" + " button 'Middle'\n" + " iframe\n" + " group\n" + " group\n" + " button 'Inside Before'\n" + " iframe\n" + " group\n" + " button 'Inside After'\n" + " button 'After'\n", + dump); +} + } // namespace content diff --git a/chromium/content/browser/android/browser_surface_texture_manager.h b/chromium/content/browser/android/browser_surface_texture_manager.h index f9fb74cccb5..639ce28b0e5 100644 --- a/chromium/content/browser/android/browser_surface_texture_manager.h +++ b/chromium/content/browser/android/browser_surface_texture_manager.h @@ -5,18 +5,18 @@ #ifndef CONTENT_BROWSER_ANDROID_BROWSER_SURFACE_TEXTURE_MANAGER_H_ #define CONTENT_BROWSER_ANDROID_BROWSER_SURFACE_TEXTURE_MANAGER_H_ -#include "content/common/android/surface_texture_manager.h" +#include "gpu/ipc/common/android/surface_texture_manager.h" #include "base/macros.h" #include "base/memory/singleton.h" -#include "content/common/android/surface_texture_peer.h" #include "content/common/content_export.h" +#include "gpu/ipc/common/android/surface_texture_peer.h" namespace content { class CONTENT_EXPORT BrowserSurfaceTextureManager - : public SurfaceTextureManager, - public SurfaceTexturePeer { + : public gpu::SurfaceTextureManager, + public gpu::SurfaceTexturePeer { public: static BrowserSurfaceTextureManager* GetInstance(); diff --git a/chromium/content/browser/android/child_process_launcher_android.cc b/chromium/content/browser/android/child_process_launcher_android.cc index 471ce31d91c..94765fbeeba 100644 --- a/chromium/content/browser/android/child_process_launcher_android.cc +++ b/chromium/content/browser/android/child_process_launcher_android.cc @@ -13,12 +13,16 @@ #include "base/android/jni_array.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "content/browser/file_descriptor_info_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/media/android/media_web_contents_observer_android.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/child_process_host_impl.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host.h" +#include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "jni/ChildProcessLauncher_jni.h" #include "media/base/android/media_player_android.h" @@ -41,7 +45,6 @@ static void SetSurfacePeer( base::ProcessHandle render_process_handle, int render_frame_id, int player_id) { -#if !defined(USE_AURA) int render_process_id = 0; RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator(); while (!it.IsAtEnd()) { @@ -83,9 +86,21 @@ static void SetSurfacePeer( gfx::ScopedJavaSurface scoped_surface(surface); player->SetVideoSurface(std::move(scoped_surface)); } -#else - NOTREACHED(); -#endif +} + +void LaunchDownloadProcess(base::CommandLine* cmd_line) { + scoped_ptr cmd_line_deleter(cmd_line); + + JNIEnv* env = AttachCurrentThread(); + DCHECK(env); + + // Create the Command line String[] + ScopedJavaLocalRef j_argv = + ToJavaArrayOfStrings(env, cmd_line->argv()); + + // TODO(qinmin): pass download parameters here. + Java_ChildProcessLauncher_startDownloadProcessIfNecessary( + env, base::android::GetApplicationContext(), j_argv.obj()); } } // anonymous namespace @@ -107,6 +122,32 @@ static void OnChildProcessStarted(JNIEnv*, delete callback; } +void StartDownloadProcessIfNecessary() { + base::FilePath exe_path = content::ChildProcessHost::GetChildPath( + content::ChildProcessHost::CHILD_NORMAL); + if (exe_path.empty()) { + NOTREACHED() << "Unable to get download process binary name."; + return; + } + base::CommandLine* cmd_line = new base::CommandLine(exe_path); + cmd_line->AppendSwitchASCII(switches::kProcessType, + switches::kDownloadProcess); + cmd_line->AppendSwitch(switches::kNoSandbox); + + const base::CommandLine browser_command_line = + *base::CommandLine::ForCurrentProcess(); + static const char* kForwardSwitches[] = { + switches::kDisableLogging, + switches::kEnableLogging, + switches::kLoggingLevel, + }; + cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches, + arraysize(kForwardSwitches)); + CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); + BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, + base::Bind(&LaunchDownloadProcess, cmd_line)); +} + void StartChildProcess( const base::CommandLine::StringVector& argv, int child_process_id, @@ -205,6 +246,13 @@ void UnregisterViewSurface(int surface_id) { Java_ChildProcessLauncher_unregisterViewSurface(env, surface_id); } +gfx::ScopedJavaSurface GetViewSurface(int surface_id) { + JNIEnv* env = AttachCurrentThread(); + DCHECK(env); + return gfx::ScopedJavaSurface::AcquireExternalSurface( + Java_ChildProcessLauncher_getViewSurface(env, surface_id).obj()); +} + void CreateSurfaceTextureSurface(int surface_texture_id, int client_id, gfx::SurfaceTexture* surface_texture) { diff --git a/chromium/content/browser/android/child_process_launcher_android.h b/chromium/content/browser/android/child_process_launcher_android.h index ea806ac5571..41cf4535704 100644 --- a/chromium/content/browser/android/child_process_launcher_android.h +++ b/chromium/content/browser/android/child_process_launcher_android.h @@ -6,11 +6,13 @@ #define CONTENT_BROWSER_ANDROID_CHILD_PROCESS_LAUNCHER_ANDROID_H_ #include + #include #include "base/callback.h" #include "base/command_line.h" #include "base/files/memory_mapped_file.h" +#include "base/memory/scoped_ptr.h" #include "base/process/process.h" #include "content/public/browser/file_descriptor_info.h" #include "ui/gl/android/scoped_java_surface.h" @@ -29,6 +31,10 @@ void StartChildProcess( const std::map& regions, const StartChildProcessCallback& callback); +// Starts the background download process if it hasn't been started. +// TODO(qinmin): pass the download parameters here and pass it to java side. +void StartDownloadProcessIfNecessary(); + // Stops a child process based on the handle returned form // StartChildProcess. void StopChildProcess(base::ProcessHandle handle); @@ -42,6 +48,8 @@ void RegisterViewSurface(int surface_id, jobject j_surface); void UnregisterViewSurface(int surface_id); +gfx::ScopedJavaSurface GetViewSurface(int surface_id); + void CreateSurfaceTextureSurface(int surface_texture_id, int client_id, gfx::SurfaceTexture* surface_texture); diff --git a/chromium/content/browser/android/composited_touch_handle_drawable.cc b/chromium/content/browser/android/composited_touch_handle_drawable.cc index 235e2eef3b4..16974e1afcc 100644 --- a/chromium/content/browser/android/composited_touch_handle_drawable.cc +++ b/chromium/content/browser/android/composited_touch_handle_drawable.cc @@ -96,7 +96,7 @@ CompositedTouchHandleDrawable::CompositedTouchHandleDrawable( jobject context) : dpi_scale_(dpi_scale), orientation_(ui::TouchHandleOrientation::UNDEFINED), - layer_(cc::UIResourceLayer::Create(Compositor::LayerSettings())) { + layer_(cc::UIResourceLayer::Create()) { g_selection_resources.Get().LoadIfNecessary(context); drawable_horizontal_padding_ratio_ = g_selection_resources.Get().GetDrawableHorizontalPaddingRatio(); diff --git a/chromium/content/browser/android/content_startup_flags.cc b/chromium/content/browser/android/content_startup_flags.cc index acda33ee494..b588deda0c9 100644 --- a/chromium/content/browser/android/content_startup_flags.cc +++ b/chromium/content/browser/android/content_startup_flags.cc @@ -10,7 +10,6 @@ #include "base/logging.h" #include "base/sys_info.h" #include "cc/base/switches.h" -#include "cc/layers/layer_settings.h" #include "content/public/browser/android/compositor.h" #include "content/public/common/content_switches.h" #include "gpu/command_buffer/service/gpu_switches.h" @@ -79,14 +78,6 @@ void SetContentCommandLineFlags(bool single_process, parsed_command_line->AppendSwitchASCII( switches::kProfilerTiming, switches::kProfilerTimingDisabledValue); } - -#if !defined(USE_AURA) - cc::LayerSettings layer_settings; - layer_settings.use_compositor_animation_timelines = - !parsed_command_line->HasSwitch( - switches::kDisableAndroidCompositorAnimationTimelines); - Compositor::SetLayerSettings(layer_settings); -#endif } } // namespace content diff --git a/chromium/content/browser/android/content_video_view.cc b/chromium/content/browser/android/content_video_view.cc index b107377c34c..4b14959ba5f 100644 --- a/chromium/content/browser/android/content_video_view.cc +++ b/chromium/content/browser/android/content_video_view.cc @@ -4,21 +4,15 @@ #include "content/browser/android/content_video_view.h" -#include "base/command_line.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" +#include "content/browser/android/content_view_core_impl.h" #include "content/browser/media/android/browser_media_player_manager.h" -#include "content/browser/power_save_blocker_impl.h" -#include "content/common/android/surface_texture_peer.h" #include "content/public/browser/user_metrics.h" #include "content/public/common/content_switches.h" #include "jni/ContentVideoView_jni.h" -#if !defined(USE_AURA) -#include "content/browser/android/content_view_core_impl.h" -#endif - using base::android::AttachCurrentThread; using base::android::CheckException; using base::android::ScopedJavaGlobalRef; @@ -51,12 +45,11 @@ ContentVideoView* ContentVideoView::GetInstance() { return g_content_video_view; } -ContentVideoView::ContentVideoView( - BrowserMediaPlayerManager* manager) - : manager_(manager), - weak_factory_(this) { +ContentVideoView::ContentVideoView(Client* client, + ContentViewCore* content_view_core) + : client_(client), weak_factory_(this) { DCHECK(!g_content_video_view); - j_content_video_view_ = CreateJavaObject(); + j_content_video_view_ = CreateJavaObject(content_view_core); g_content_video_view = this; } @@ -85,7 +78,7 @@ void ContentVideoView::OnMediaPlayerError(int error_type) { ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { Java_ContentVideoView_onMediaPlayerError(env, content_video_view.obj(), - error_type); + error_type); } } @@ -94,23 +87,35 @@ void ContentVideoView::OnVideoSizeChanged(int width, int height) { ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { Java_ContentVideoView_onVideoSizeChanged(env, content_video_view.obj(), - width, height); + width, height); } } -void ContentVideoView::OnPlaybackComplete() { +void ContentVideoView::ExitFullscreen() { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef content_video_view = GetJavaObject(env); - if (!content_video_view.is_null()) { - Java_ContentVideoView_onPlaybackComplete(env, content_video_view.obj()); - } + bool release_media_player = false; + if (!content_video_view.is_null()) + Java_ContentVideoView_exitFullscreen(env, content_video_view.obj(), + release_media_player); } -void ContentVideoView::OnExitFullscreen() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef content_video_view = GetJavaObject(env); - if (!content_video_view.is_null()) - Java_ContentVideoView_onExitFullscreen(env, content_video_view.obj()); +ScopedJavaLocalRef ContentVideoView::GetJavaObject(JNIEnv* env) { + return j_content_video_view_.get(env); +} + +void ContentVideoView::SetSurface(JNIEnv*, + const JavaParamRef&, + const JavaParamRef& surface) { + client_->SetVideoSurface( + gfx::ScopedJavaSurface::AcquireExternalSurface(surface)); +} + +void ContentVideoView::DidExitFullscreen(JNIEnv*, + const JavaParamRef&, + jboolean release_media_player) { + j_content_video_view_.reset(); + client_->DidExitFullscreen(release_media_player); } void ContentVideoView::RecordFullscreenPlayback(JNIEnv*, @@ -151,63 +156,11 @@ void ContentVideoView::RecordExitFullscreenPlayback( } } -void ContentVideoView::UpdateMediaMetadata() { +JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject( + ContentViewCore* content_view_core) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef content_video_view = GetJavaObject(env); - if (content_video_view.is_null()) - return; - - media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); - if (player && player->IsPlayerReady()) { - Java_ContentVideoView_onUpdateMediaMetadata( - env, content_video_view.obj(), player->GetVideoWidth(), - player->GetVideoHeight(), - static_cast(player->GetDuration().InMilliseconds()), - player->CanPause(),player->CanSeekForward(), player->CanSeekBackward()); - } -} - -bool ContentVideoView::IsPlaying(JNIEnv*, const JavaParamRef& obj) { - media::MediaPlayerAndroid* player = manager_->GetFullscreenPlayer(); - return player ? player->IsPlaying() : false; -} - -void ContentVideoView::ExitFullscreen(JNIEnv*, - const JavaParamRef&, - jboolean release_media_player) { - j_content_video_view_.reset(); - manager_->ExitFullscreen(release_media_player); -} - -void ContentVideoView::SetSurface(JNIEnv* env, - const JavaParamRef& obj, - const JavaParamRef& surface) { - manager_->SetVideoSurface( - gfx::ScopedJavaSurface::AcquireExternalSurface(surface)); -} - -void ContentVideoView::RequestMediaMetadata(JNIEnv* env, - const JavaParamRef& obj) { - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&ContentVideoView::UpdateMediaMetadata, - weak_factory_.GetWeakPtr())); -} - -ScopedJavaLocalRef ContentVideoView::GetJavaObject(JNIEnv* env) { - return j_content_video_view_.get(env); -} - -JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject() { - - JNIEnv* env = AttachCurrentThread(); - base::android::ScopedJavaLocalRef j_content_view_core; - -#if !defined(USE_AURA) - ContentViewCore* content_view_core = manager_->GetContentViewCore(); - j_content_view_core = content_view_core->GetJavaObject(); -#endif - + base::android::ScopedJavaLocalRef j_content_view_core = + content_view_core->GetJavaObject(); if (j_content_view_core.is_null()) return JavaObjectWeakGlobalRef(env, nullptr); diff --git a/chromium/content/browser/android/content_video_view.h b/chromium/content/browser/android/content_video_view.h index 7959cd60880..292f818d927 100644 --- a/chromium/content/browser/android/content_video_view.h +++ b/chromium/content/browser/android/content_video_view.h @@ -10,60 +10,69 @@ #include "base/android/jni_weak_ref.h" #include "base/android/scoped_java_ref.h" #include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "ui/gfx/native_widget_types.h" +#include "content/public/browser/android/content_view_core.h" +#include "ui/gl/android/scoped_java_surface.h" namespace content { -class BrowserMediaPlayerManager; - // Native mirror of ContentVideoView.java. This class is responsible for -// creating the Java video view and pass all the player status change to -// it. It accepts media control from Java class, and forwards it to -// MediaPlayerManagerImpl. +// creating the Java video view and passing changes in player status to it. +// This class must be used on the UI thread. class ContentVideoView { public: - // Construct a ContentVideoView object. The |manager| will handle all the - // playback controls from the Java class. - explicit ContentVideoView(BrowserMediaPlayerManager* manager); + static bool RegisterContentVideoView(JNIEnv* env); + + // Returns the singleton object or NULL. + static ContentVideoView* GetInstance(); + + class Client { + public: + Client() {} + // For receiving notififcations when the SurfaceView surface is created and + // destroyed. When |surface.IsEmpty()| the surface was destroyed and + // the client should not hold any references to it once this returns. + virtual void SetVideoSurface(gfx::ScopedJavaSurface surface) = 0; + // Called after the ContentVideoView has been hidden because we're exiting + // fullscreen. + virtual void DidExitFullscreen(bool release_media_player) = 0; + + protected: + ~Client() {} + + DISALLOW_COPY_AND_ASSIGN(Client); + }; + + explicit ContentVideoView(Client* client, ContentViewCore* content_view_core); ~ContentVideoView(); // To open another video on existing ContentVideoView. void OpenVideo(); - static bool RegisterContentVideoView(JNIEnv* env); - static void KeepScreenOn(bool screen_on); + // Display an error dialog to the user. + void OnMediaPlayerError(int error_type); - // Return the singleton object or NULL. - static ContentVideoView* GetInstance(); + // Update the video size. The video will not be visible until this is called. + void OnVideoSizeChanged(int width, int height); - // Getter method called by the Java class to get the media information. - bool IsPlaying(JNIEnv*, const base::android::JavaParamRef& obj); - void RequestMediaMetadata(JNIEnv*, - const base::android::JavaParamRef& obj); + // Exit fullscreen and notify |client_| with |DidExitFullscreen|. + void ExitFullscreen(); - // Called when the Java fullscreen view is destroyed. If - // |release_media_player| is true, |manager_| needs to release the player - // as we are quitting the app. - void ExitFullscreen(JNIEnv*, - const base::android::JavaParamRef&, - jboolean release_media_player); + // Returns the corresponding ContentVideoView Java object if any. + base::android::ScopedJavaLocalRef GetJavaObject(JNIEnv* env); - // Called by the Java class to pass the surface object to the player. - void SetSurface(JNIEnv*, + // Called by the Java class when the surface changes. + void SetSurface(JNIEnv* env, const base::android::JavaParamRef& obj, const base::android::JavaParamRef& surface); - // Method called by |manager_| to inform the Java class about player status - // change. - void UpdateMediaMetadata(); - void OnMediaPlayerError(int errorType); - void OnVideoSizeChanged(int width, int height); - void OnPlaybackComplete(); - void OnExitFullscreen(); + // Called when the Java fullscreen view is destroyed. If + // |release_media_player| is true, |client_| needs to release the player + // as we are quitting the app. + void DidExitFullscreen(JNIEnv*, + const base::android::JavaParamRef&, + jboolean release_media_player); // Functions called to record fullscreen playback UMA metrics. void RecordFullscreenPlayback(JNIEnv*, @@ -77,18 +86,13 @@ class ContentVideoView { long playback_duration_in_milliseconds_before_orientation_change, long playback_duration_in_milliseconds_after_orientation_change); - // Return the corresponing ContentVideoView Java object if any. - base::android::ScopedJavaLocalRef GetJavaObject(JNIEnv* env); - private: // Creates the corresponding ContentVideoView Java object. - JavaObjectWeakGlobalRef CreateJavaObject(); + JavaObjectWeakGlobalRef CreateJavaObject(ContentViewCore* content_view_core); - // Object that manages the fullscreen media player. It is responsible for - // handling all the playback controls. - BrowserMediaPlayerManager* manager_; + Client* client_; - // Weak reference of corresponding Java object. + // Weak reference to corresponding Java object. JavaObjectWeakGlobalRef j_content_video_view_; // Weak pointer for posting tasks. diff --git a/chromium/content/browser/android/content_view_core_impl.cc b/chromium/content/browser/android/content_view_core_impl.cc index 965c21e95f0..2322e763408 100644 --- a/chromium/content/browser/android/content_view_core_impl.cc +++ b/chromium/content/browser/android/content_view_core_impl.cc @@ -19,6 +19,7 @@ #include "cc/layers/layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/output/begin_frame_args.h" +#include "cc/output/viewport_selection_bound.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/android/gesture_event_type.h" #include "content/browser/android/interstitial_page_delegate_android.h" @@ -55,6 +56,7 @@ #include "ui/android/view_android.h" #include "ui/android/window_android.h" #include "ui/events/android/motion_event_android.h" +#include "ui/events/blink/blink_event_util.h" #include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/size_conversions.h" @@ -212,7 +214,7 @@ ContentViewCoreImpl::ContentViewCoreImpl( : WebContentsObserver(web_contents), java_ref_(env, obj), web_contents_(static_cast(web_contents)), - root_layer_(cc::SolidColorLayer::Create(Compositor::LayerSettings())), + root_layer_(cc::SolidColorLayer::Create()), page_scale_(1), dpi_scale_(ui::GetScaleFactorForNativeView(this)), window_android_(window_android), @@ -247,8 +249,22 @@ ContentViewCoreImpl::ContentViewCoreImpl( InitWebContents(); } +void ContentViewCoreImpl::AddObserver( + ContentViewCoreImplObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ContentViewCoreImpl::RemoveObserver( + ContentViewCoreImplObserver* observer) { + observer_list_.RemoveObserver(observer); +} + ContentViewCoreImpl::~ContentViewCoreImpl() { root_layer_->RemoveFromParent(); + FOR_EACH_OBSERVER(ContentViewCoreImplObserver, + observer_list_, + OnContentViewCoreDestroyed()); + observer_list_.Clear(); JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef j_obj = java_ref_.get(env); @@ -259,6 +275,24 @@ ContentViewCoreImpl::~ContentViewCoreImpl() { } } +void ContentViewCoreImpl::UpdateWindowAndroid( + JNIEnv* env, + const base::android::JavaParamRef& obj, + jlong window_android) { + if (window_android) { + DCHECK(!window_android_); + window_android_ = reinterpret_cast(window_android); + FOR_EACH_OBSERVER(ContentViewCoreImplObserver, + observer_list_, + OnAttachedToWindow()); + } else { + FOR_EACH_OBSERVER(ContentViewCoreImplObserver, + observer_list_, + OnDetachedFromWindow()); + window_android_ = NULL; + } +} + base::android::ScopedJavaLocalRef ContentViewCoreImpl::GetWebContentsAndroid(JNIEnv* env, const JavaParamRef& obj) { @@ -387,10 +421,11 @@ void ContentViewCoreImpl::UpdateFrameInfo( const gfx::SizeF& viewport_size, const gfx::Vector2dF& controls_offset, const gfx::Vector2dF& content_offset, - bool is_mobile_optimized_hint) { + bool is_mobile_optimized_hint, + const cc::ViewportSelectionBound& selection_start) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) + if (obj.is_null() || !window_android_) return; window_android_->set_content_offset( @@ -398,6 +433,18 @@ void ContentViewCoreImpl::UpdateFrameInfo( page_scale_ = page_scale_factor; + // The CursorAnchorInfo API in Android only supports zero width selection + // bounds. + const jboolean has_insertion_marker = + selection_start.type == cc::SELECTION_BOUND_CENTER; + const jboolean is_insertion_marker_visible = selection_start.visible; + const jfloat insertion_marker_horizontal = + has_insertion_marker ? selection_start.edge_top.x() : 0.0f; + const jfloat insertion_marker_top = + has_insertion_marker ? selection_start.edge_top.y() : 0.0f; + const jfloat insertion_marker_bottom = + has_insertion_marker ? selection_start.edge_bottom.y() : 0.0f; + Java_ContentViewCore_updateFrameInfo( env, obj.obj(), scroll_offset.x(), @@ -411,7 +458,12 @@ void ContentViewCoreImpl::UpdateFrameInfo( viewport_size.height(), controls_offset.y(), content_offset.y(), - is_mobile_optimized_hint); + is_mobile_optimized_hint, + has_insertion_marker, + is_insertion_marker_visible, + insertion_marker_horizontal, + insertion_marker_top, + insertion_marker_bottom); } void ContentViewCoreImpl::SetTitle(const base::string16& title) { @@ -718,6 +770,22 @@ ScopedJavaLocalRef ContentViewCoreImpl::GetContext() const { return Java_ContentViewCore_getContext(env, obj.obj()); } +gfx::Size ContentViewCoreImpl::GetViewSizeWithOSKHidden() const { + gfx::Size size_pix; + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (j_obj.is_null()) + return size_pix = gfx::Size(); + size_pix = gfx::Size( + Java_ContentViewCore_getViewportWidthPix(env, j_obj.obj()), + Java_ContentViewCore_getViewportHeightWithOSKHiddenPix(env, j_obj.obj())); + + gfx::Size size_dip = gfx::ScaleToCeiledSize(size_pix, 1.0f / dpi_scale()); + if (DoTopControlsShrinkBlinkSize()) + size_dip.Enlarge(0, -GetTopControlsHeightDip()); + return size_dip; +} + gfx::Size ContentViewCoreImpl::GetViewSize() const { gfx::Size size = GetViewportSizeDip(); if (DoTopControlsShrinkBlinkSize()) @@ -949,7 +1017,8 @@ jboolean ContentViewCoreImpl::SendMouseMoveEvent( const JavaParamRef& obj, jlong time_ms, jfloat x, - jfloat y) { + jfloat y, + jint tool_type) { RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); if (!rwhv) return false; @@ -957,7 +1026,8 @@ jboolean ContentViewCoreImpl::SendMouseMoveEvent( blink::WebMouseEvent event = WebMouseEventBuilder::Build( WebInputEvent::MouseMove, blink::WebMouseEvent::ButtonNone, - time_ms / 1000.0, x / dpi_scale(), y / dpi_scale(), 0, 1); + time_ms / 1000.0, x / dpi_scale(), y / dpi_scale(), 0, 1, + ui::ToWebPointerType(static_cast(tool_type))); rwhv->SendMouseEvent(event); return true; @@ -1154,15 +1224,6 @@ void ContentViewCoreImpl::SelectBetweenCoordinates( gfx::PointF(x2 / dpi_scale(), y2 / dpi_scale())); } -void ContentViewCoreImpl::MoveCaret(JNIEnv* env, - const JavaParamRef& obj, - jfloat x, - jfloat y) { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - if (rwhv) - rwhv->MoveCaret(gfx::Point(x / dpi_scale_, y / dpi_scale_)); -} - void ContentViewCoreImpl::DismissTextHandles(JNIEnv* env, const JavaParamRef& obj) { RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); @@ -1239,9 +1300,7 @@ void ContentViewCoreImpl::WasResized(JNIEnv* env, root_layer_->SetBounds(physical_size); if (view) { - RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( - view->GetRenderWidgetHost()); - host->SendScreenRects(); + web_contents_->SendScreenRects(); view->WasResized(); } } @@ -1431,10 +1490,6 @@ void ContentViewCoreImpl::OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip) { static_cast(y_dip * dpi_scale())); } -float ContentViewCoreImpl::GetScaleFactor() const { - return page_scale_ * dpi_scale_; -} - void ContentViewCoreImpl::OnSmartClipDataExtracted( const base::string16& text, const base::string16& html, diff --git a/chromium/content/browser/android/content_view_core_impl.h b/chromium/content/browser/android/content_view_core_impl.h index 2e0b22694ee..31635faca33 100644 --- a/chromium/content/browser/android/content_view_core_impl.h +++ b/chromium/content/browser/android/content_view_core_impl.h @@ -16,6 +16,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/process/process.h" +#include "content/browser/android/content_view_core_impl_observer.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/android/content_view_core.h" @@ -27,6 +28,10 @@ #include "ui/gfx/geometry/rect_f.h" #include "url/gurl.h" +namespace cc { +struct ViewportSelectionBound; +} + namespace ui { class WindowAndroid; } @@ -69,6 +74,9 @@ class ContentViewCoreImpl : public ContentViewCore, int start_offset, int end_offset)>& callback) override; + void AddObserver(ContentViewCoreImplObserver* observer); + void RemoveObserver(ContentViewCoreImplObserver* observer); + // ViewAndroid implementation base::android::ScopedJavaLocalRef GetViewAndroidDelegate() const override; @@ -84,6 +92,9 @@ class ContentViewCoreImpl : public ContentViewCore, JNIEnv* env, const base::android::JavaParamRef& obj); + void UpdateWindowAndroid(JNIEnv* env, + const base::android::JavaParamRef& obj, + jlong window_android); void OnJavaContentViewCoreDestroyed( JNIEnv* env, const base::android::JavaParamRef& obj); @@ -134,7 +145,8 @@ class ContentViewCoreImpl : public ContentViewCore, const base::android::JavaParamRef& obj, jlong time_ms, jfloat x, - jfloat y); + jfloat y, + jint tool_type); jboolean SendMouseWheelEvent(JNIEnv* env, const base::android::JavaParamRef& obj, jlong time_ms, @@ -207,10 +219,6 @@ class ContentViewCoreImpl : public ContentViewCore, jfloat y1, jfloat x2, jfloat y2); - void MoveCaret(JNIEnv* env, - const base::android::JavaParamRef& obj, - jfloat x, - jfloat y); void DismissTextHandles(JNIEnv* env, const base::android::JavaParamRef& obj); void SetTextHandlesTemporarilyHidden( @@ -236,7 +244,6 @@ class ContentViewCoreImpl : public ContentViewCore, jboolean focused); jint GetBackgroundColor(JNIEnv* env, jobject obj); - void SetBackgroundColor(JNIEnv* env, jobject obj, jint color); void SetAllowJavascriptInterfacesInspection( JNIEnv* env, const base::android::JavaParamRef& obj, @@ -313,7 +320,8 @@ class ContentViewCoreImpl : public ContentViewCore, const gfx::SizeF& viewport_size, const gfx::Vector2dF& controls_offset, const gfx::Vector2dF& content_offset, - bool is_mobile_optimized_hint); + bool is_mobile_optimized_hint, + const cc::ViewportSelectionBound& selection_start); void ForceUpdateImeAdapter(long native_ime_adapter); void UpdateImeAdapter(long native_ime_adapter, @@ -363,6 +371,8 @@ class ContentViewCoreImpl : public ContentViewCore, // Returns the viewport size after accounting for the viewport offset. gfx::Size GetViewSize() const; + gfx::Size GetViewSizeWithOSKHidden() const; + void SetAccessibilityEnabledInternal(bool enabled); bool IsFullscreenRequiredForOrientationLock() const; @@ -386,8 +396,6 @@ class ContentViewCoreImpl : public ContentViewCore, void OnShowUnhandledTapUIIfNeeded(int x_dip, int y_dip); - // returns page density (dpi) X page scale - float GetScaleFactor() const; private: class ContentViewUserData; @@ -454,6 +462,9 @@ class ContentViewCoreImpl : public ContentViewCore, // The owning window that has a hold of main application activity. ui::WindowAndroid* window_android_; + // Observer to notify of lifecyle changes. + base::ObserverList observer_list_; + // The cache of device's current orientation set from Java side, this value // will be sent to Renderer once it is ready. int device_orientation_; diff --git a/chromium/content/browser/android/content_view_core_impl_observer.h b/chromium/content/browser/android/content_view_core_impl_observer.h new file mode 100644 index 00000000000..d27735e9dc0 --- /dev/null +++ b/chromium/content/browser/android/content_view_core_impl_observer.h @@ -0,0 +1,22 @@ +// 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 CONTENT_BROWSER_ANDROID_CONTENT_VIEW_CORE_IMPL_OBSERVER_H +#define CONTENT_BROWSER_ANDROID_CONTENT_VIEW_CORE_IMPL_OBSERVER_H + +namespace content { + +class ContentViewCoreImplObserver { + public: + virtual void OnContentViewCoreDestroyed() = 0; + virtual void OnAttachedToWindow() = 0; + virtual void OnDetachedFromWindow() = 0; + + protected: + virtual ~ContentViewCoreImplObserver() {} +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_CONTENT_VIEW_CORE_IMPL_OBSERVER_H diff --git a/chromium/content/browser/android/content_view_render_view.cc b/chromium/content/browser/android/content_view_render_view.cc index 24fdbc2f020..be3064e05eb 100644 --- a/chromium/content/browser/android/content_view_render_view.cc +++ b/chromium/content/browser/android/content_view_render_view.cc @@ -99,14 +99,7 @@ void ContentViewRenderView::SetOverlayVideoMode( const JavaParamRef& obj, bool enabled) { compositor_->SetHasTransparentBackground(enabled); - SetNeedsComposite(env, obj); -} - -void ContentViewRenderView::SetNeedsComposite( - JNIEnv* env, - const JavaParamRef& obj) { - if (compositor_) - compositor_->SetNeedsComposite(); + compositor_->SetNeedsComposite(); } void ContentViewRenderView::UpdateLayerTreeHost() { @@ -124,11 +117,4 @@ void ContentViewRenderView::InitCompositor() { compositor_.reset(Compositor::Create(this, root_window_)); } -jlong ContentViewRenderView::GetUIResourceProvider( - JNIEnv* env, - const JavaParamRef& obj) { - if (!compositor_) - return 0; - return reinterpret_cast(&compositor_->GetUIResourceProvider()); -} } // namespace content diff --git a/chromium/content/browser/android/content_view_render_view.h b/chromium/content/browser/android/content_view_render_view.h index c571de0fd4e..50f3b9c4f83 100644 --- a/chromium/content/browser/android/content_view_render_view.h +++ b/chromium/content/browser/android/content_view_render_view.h @@ -48,13 +48,6 @@ class ContentViewRenderView : public CompositorClient { void SetOverlayVideoMode(JNIEnv* env, const base::android::JavaParamRef& obj, bool enabled); - void SetNeedsComposite(JNIEnv* env, - const base::android::JavaParamRef& obj); - - // TODO(yusufo): Remove this once the compositor code is - // refactored to use a unified system. - jlong GetUIResourceProvider(JNIEnv* env, - const base::android::JavaParamRef& obj); // CompositorClient implementation void UpdateLayerTreeHost() override; diff --git a/chromium/content/browser/android/download_controller_android_impl.cc b/chromium/content/browser/android/download_controller_android_impl.cc index feb8de30152..768a599f6bd 100644 --- a/chromium/content/browser/android/download_controller_android_impl.cc +++ b/chromium/content/browser/android/download_controller_android_impl.cc @@ -32,6 +32,7 @@ #include "content/public/common/content_client.h" #include "content/public/common/referrer.h" #include "jni/DownloadController_jni.h" +#include "net/base/filename_util.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_store.h" #include "net/http/http_content_disposition.h" @@ -97,6 +98,17 @@ void CreateContextMenuDownload(int render_process_id, dlm->DownloadUrl(std::move(dl_params)); } +// Check if an interrupted download item can be auto resumed. +bool IsInterruptedDownloadAutoResumable(content::DownloadItem* download_item) { + int interrupt_reason = download_item->GetLastReason(); + DCHECK_NE(interrupt_reason, content::DOWNLOAD_INTERRUPT_REASON_NONE); + return + interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT || + interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED || + interrupt_reason == + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED; +} + } // namespace namespace content { @@ -211,6 +223,11 @@ void DownloadControllerAndroidImpl::AcquireFileAccessPermission( env, GetJavaObject()->Controller(env).obj(), view.obj(), callback_id); } +void DownloadControllerAndroidImpl::SetDefaultDownloadFileName( + const std::string& file_name) { + default_file_name_ = file_name; +} + bool DownloadControllerAndroidImpl::HasFileAccessPermission( ScopedJavaLocalRef j_content_view_core) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -222,7 +239,8 @@ bool DownloadControllerAndroidImpl::HasFileAccessPermission( } void DownloadControllerAndroidImpl::CreateGETDownload( - int render_process_id, int render_view_id, int request_id) { + int render_process_id, int render_view_id, int request_id, + bool must_download) { DCHECK_CURRENTLY_ON(BrowserThread::IO); GlobalRequestID global_id(render_process_id, request_id); @@ -232,7 +250,7 @@ void DownloadControllerAndroidImpl::CreateGETDownload( GetDownloadInfoCB cb = base::Bind( &DownloadControllerAndroidImpl::StartAndroidDownload, base::Unretained(this), render_process_id, - render_view_id); + render_view_id, must_download); PrepareDownloadInfo( global_id, @@ -326,7 +344,7 @@ void DownloadControllerAndroidImpl::StartDownloadOnUIThread( } void DownloadControllerAndroidImpl::StartAndroidDownload( - int render_process_id, int render_view_id, + int render_process_id, int render_view_id, bool must_download, const DownloadInfoAndroid& info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -346,7 +364,7 @@ void DownloadControllerAndroidImpl::StartAndroidDownload( web_contents, base::Bind(&DownloadControllerAndroidImpl::StartAndroidDownload, base::Unretained(this), render_process_id, render_view_id, - info))); + must_download, info))); return; } @@ -354,11 +372,11 @@ void DownloadControllerAndroidImpl::StartAndroidDownload( web_contents, base::Bind(&DownloadControllerAndroidImpl::StartAndroidDownloadInternal, base::Unretained(this), render_process_id, render_view_id, - info)); + must_download, info)); } void DownloadControllerAndroidImpl::StartAndroidDownloadInternal( - int render_process_id, int render_view_id, + int render_process_id, int render_view_id, bool must_download, const DownloadInfoAndroid& info, bool allowed) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!allowed) @@ -388,17 +406,20 @@ void DownloadControllerAndroidImpl::StartAndroidDownloadInternal( ScopedJavaLocalRef jreferer = ConvertUTF8ToJavaString(env, info.referer); - // Try parsing the content disposition header to get a - // explicitly specified filename if available. - net::HttpContentDisposition header(info.content_disposition, ""); + // net::GetSuggestedFilename will fallback to "download" as filename. ScopedJavaLocalRef jfilename = - ConvertUTF8ToJavaString(env, header.filename()); + base::android::ConvertUTF16ToJavaString( + env, net::GetSuggestedFilename(info.url, info.content_disposition, + std::string(), // referrer_charset + std::string(), // suggested_name + info.original_mime_type, + default_file_name_)); Java_DownloadController_newHttpGetDownload( env, GetJavaObject()->Controller(env).obj(), view.obj(), jurl.obj(), juser_agent.obj(), jcontent_disposition.obj(), jmime_type.obj(), jcookie.obj(), jreferer.obj(), info.has_user_gesture, jfilename.obj(), - info.total_bytes); + info.total_bytes, must_download); } void DownloadControllerAndroidImpl::OnDownloadStarted( @@ -433,6 +454,8 @@ void DownloadControllerAndroidImpl::OnDownloadUpdated(DownloadItem* item) { OnDangerousDownload(item); JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef jguid = + ConvertUTF8ToJavaString(env, item->GetGuid()); ScopedJavaLocalRef jurl = ConvertUTF8ToJavaString(env, item->GetURL().spec()); ScopedJavaLocalRef jmime_type = @@ -441,17 +464,22 @@ void DownloadControllerAndroidImpl::OnDownloadUpdated(DownloadItem* item) { ConvertUTF8ToJavaString(env, item->GetTargetFilePath().value()); ScopedJavaLocalRef jfilename = ConvertUTF8ToJavaString( env, item->GetTargetFilePath().BaseName().value()); + ScopedJavaLocalRef joriginal_url = + ConvertUTF8ToJavaString(env, item->GetOriginalUrl().spec()); + ScopedJavaLocalRef jreferrer_url = + ConvertUTF8ToJavaString(env, item->GetReferrerUrl().spec()); switch (item->GetState()) { case DownloadItem::IN_PROGRESS: { base::TimeDelta time_delta; item->TimeRemaining(&time_delta); Java_DownloadController_onDownloadUpdated( - env, GetJavaObject()->Controller(env).obj(), - base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(), - jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true, - item->GetId(), item->PercentComplete(), time_delta.InMilliseconds(), - item->HasUserGesture()); + env, GetJavaObject()->Controller(env).obj(), jurl.obj(), + jmime_type.obj(), jfilename.obj(), jpath.obj(), + item->GetReceivedBytes(), item->GetId(), jguid.obj(), + item->PercentComplete(), time_delta.InMilliseconds(), + item->HasUserGesture(), item->IsPaused(), + item->GetBrowserContext()->IsOffTheRecord()); break; } case DownloadItem::COMPLETE: @@ -461,22 +489,27 @@ void DownloadControllerAndroidImpl::OnDownloadUpdated(DownloadItem* item) { // Call onDownloadCompleted Java_DownloadController_onDownloadCompleted( - env, GetJavaObject()->Controller(env).obj(), - base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(), - jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true, - item->GetId(), item->HasUserGesture()); + env, GetJavaObject()->Controller(env).obj(), jurl.obj(), + jmime_type.obj(), jfilename.obj(), jpath.obj(), + item->GetReceivedBytes(), item->GetId(), jguid.obj(), + joriginal_url.obj(), jreferrer_url.obj(), item->HasUserGesture()); break; case DownloadItem::CANCELLED: - // TODO(shashishekhar): An interrupted download can be resumed. Android - // currently does not support resumable downloads. Add handling for - // interrupted case based on item->CanResume(). + Java_DownloadController_onDownloadCancelled( + env, GetJavaObject()->Controller(env).obj(), item->GetId(), + jguid.obj()); + break; case DownloadItem::INTERRUPTED: - // Call onDownloadCompleted with success = false. - Java_DownloadController_onDownloadCompleted( - env, GetJavaObject()->Controller(env).obj(), - base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(), - jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), false, - item->GetId(), item->HasUserGesture()); + // When device loses/changes network, we get a NETWORK_TIMEOUT, + // NETWORK_FAILED or NETWORK_DISCONNECTED error. Download should auto + // resume in this case. + Java_DownloadController_onDownloadInterrupted( + env, GetJavaObject()->Controller(env).obj(), jurl.obj(), + jmime_type.obj(), jfilename.obj(), jpath.obj(), + item->GetReceivedBytes(), item->GetId(), jguid.obj(), + item->CanResume(), IsInterruptedDownloadAutoResumable(item), + item->GetBrowserContext()->IsOffTheRecord()); + item->RemoveObserver(this); break; case DownloadItem::MAX_DOWNLOAD_STATE: NOTREACHED(); @@ -487,12 +520,14 @@ void DownloadControllerAndroidImpl::OnDangerousDownload(DownloadItem* item) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef jfilename = ConvertUTF8ToJavaString( env, item->GetTargetFilePath().BaseName().value()); + ScopedJavaLocalRef jguid = + ConvertUTF8ToJavaString(env, item->GetGuid()); ScopedJavaLocalRef view_core = GetContentViewCoreFromWebContents( item->GetWebContents()); if (!view_core.is_null()) { Java_DownloadController_onDangerousDownload( env, GetJavaObject()->Controller(env).obj(), view_core.obj(), - jfilename.obj(), item->GetId()); + jfilename.obj(), jguid.obj()); } } @@ -532,12 +567,14 @@ void DownloadControllerAndroidImpl::StartContextMenuDownload( } void DownloadControllerAndroidImpl::DangerousDownloadValidated( - WebContents* web_contents, int download_id, bool accept) { + WebContents* web_contents, + const std::string& download_guid, + bool accept) { if (!web_contents) return; DownloadManagerImpl* dlm = static_cast( BrowserContext::GetDownloadManager(web_contents->GetBrowserContext())); - DownloadItem* item = dlm->GetDownload(download_id); + DownloadItem* item = dlm->GetDownloadByGuid(download_guid); if (!item) return; if (accept) diff --git a/chromium/content/browser/android/download_controller_android_impl.h b/chromium/content/browser/android/download_controller_android_impl.h index 36c108e349d..157f306300d 100644 --- a/chromium/content/browser/android/download_controller_android_impl.h +++ b/chromium/content/browser/android/download_controller_android_impl.h @@ -30,7 +30,6 @@ #include "base/memory/scoped_vector.h" #include "base/memory/singleton.h" #include "content/public/browser/android/download_controller_android.h" -#include "content/public/browser/download_item.h" #include "net/cookies/cookie_monster.h" #include "url/gurl.h" @@ -44,8 +43,7 @@ class DeferredDownloadObserver; class RenderViewHost; class WebContents; -class DownloadControllerAndroidImpl : public DownloadControllerAndroid, - public DownloadItem::Observer { +class DownloadControllerAndroidImpl : public DownloadControllerAndroid { public: static DownloadControllerAndroidImpl* GetInstance(); @@ -61,6 +59,7 @@ class DownloadControllerAndroidImpl : public DownloadControllerAndroid, void AcquireFileAccessPermission( WebContents* web_contents, const AcquireFileAccessPermissionCallback& callback) override; + void SetDefaultDownloadFileName(const std::string& file_name) override; private: // Used to store all the information about an Android download. @@ -96,14 +95,15 @@ class DownloadControllerAndroidImpl : public DownloadControllerAndroid, // DownloadControllerAndroid implementation. void CreateGETDownload(int render_process_id, int render_view_id, - int request_id) override; + int request_id, + bool must_download) override; void OnDownloadStarted(DownloadItem* download_item) override; void StartContextMenuDownload(const ContextMenuParams& params, WebContents* web_contents, bool is_link, const std::string& extra_headers) override; void DangerousDownloadValidated(WebContents* web_contents, - int download_id, + const std::string& download_guid, bool accept) override; // DownloadItem::Observer interface. @@ -127,9 +127,11 @@ class DownloadControllerAndroidImpl : public DownloadControllerAndroid, const DownloadInfoAndroid& info); void StartAndroidDownload(int render_process_id, int render_view_id, + bool must_download, const DownloadInfoAndroid& info); void StartAndroidDownloadInternal(int render_process_id, int render_view_id, + bool must_download, const DownloadInfoAndroid& info, bool allowed); @@ -144,6 +146,8 @@ class DownloadControllerAndroidImpl : public DownloadControllerAndroid, JavaObject* java_object_; + std::string default_file_name_; + ScopedVector deferred_downloads_; DISALLOW_COPY_AND_ASSIGN(DownloadControllerAndroidImpl); diff --git a/chromium/content/browser/android/in_process/context_provider_in_process.cc b/chromium/content/browser/android/in_process/context_provider_in_process.cc index 9f4fbc2d827..e7cbb65e0f2 100644 --- a/chromium/content/browser/android/in_process/context_provider_in_process.cc +++ b/chromium/content/browser/android/in_process/context_provider_in_process.cc @@ -11,7 +11,7 @@ #include "base/callback_helpers.h" #include "base/strings/stringprintf.h" #include "cc/output/managed_memory_policy.h" -#include "content/common/gpu/client/grcontext_for_webgraphicscontext3d.h" +#include "content/common/gpu/client/grcontext_for_gles2_interface.h" #include "gpu/blink/webgraphicscontext3d_in_process_command_buffer_impl.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "third_party/skia/include/gpu/GrContext.h" @@ -25,11 +25,11 @@ class ContextProviderInProcess::LostContextCallbackProxy public: explicit LostContextCallbackProxy(ContextProviderInProcess* provider) : provider_(provider) { - provider_->WebContext3DImpl()->setContextLostCallback(this); + provider_->context3d_->setContextLostCallback(this); } ~LostContextCallbackProxy() override { - provider_->WebContext3DImpl()->setContextLostCallback(NULL); + provider_->context3d_->setContextLostCallback(nullptr); } void onContextLost() override { @@ -45,19 +45,16 @@ scoped_refptr ContextProviderInProcess::Create( scoped_ptr context3d, const std::string& debug_name) { if (!context3d) - return NULL; + return nullptr; return new ContextProviderInProcess(std::move(context3d), debug_name); } ContextProviderInProcess::ContextProviderInProcess( scoped_ptr context3d, const std::string& debug_name) - : debug_name_(debug_name) { + : context3d_(std::move(context3d)), debug_name_(debug_name) { DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(context3d); - gr_interface_ = skia::AdoptRef( - new GrGLInterfaceForWebGraphicsContext3D(std::move(context3d))); - DCHECK(gr_interface_->WebContext3D()); + DCHECK(context3d_); context_thread_checker_.DetachFromThread(); } @@ -70,20 +67,11 @@ blink::WebGraphicsContext3D* ContextProviderInProcess::WebContext3D() { DCHECK(lost_context_callback_proxy_); // Is bound to thread. DCHECK(context_thread_checker_.CalledOnValidThread()); - return WebContext3DImpl(); -} - -gpu_blink::WebGraphicsContext3DInProcessCommandBufferImpl* - ContextProviderInProcess::WebContext3DImpl() { - DCHECK(gr_interface_->WebContext3D()); - - return - static_cast( - gr_interface_->WebContext3D()); + return context3d_.get(); } bool ContextProviderInProcess::BindToCurrentThread() { - DCHECK(WebContext3DImpl()); + DCHECK(context3d_); // This is called on the thread the context will be used. DCHECK(context_thread_checker_.CalledOnValidThread()); @@ -91,16 +79,14 @@ bool ContextProviderInProcess::BindToCurrentThread() { if (lost_context_callback_proxy_) return true; - if (!WebContext3DImpl()->InitializeOnCurrentThread()) + if (!context3d_->InitializeOnCurrentThread()) return false; - gr_interface_->BindToCurrentThread(); InitializeCapabilities(); const std::string unique_context_name = - base::StringPrintf("%s-%p", debug_name_.c_str(), WebContext3DImpl()); - WebContext3DImpl()->traceBeginCHROMIUM("gpu_toplevel", - unique_context_name.c_str()); + base::StringPrintf("%s-%p", debug_name_.c_str(), context3d_.get()); + ContextGL()->TraceBeginCHROMIUM("gpu_toplevel", unique_context_name.c_str()); lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this)); return true; @@ -111,9 +97,9 @@ void ContextProviderInProcess::DetachFromThread() { } void ContextProviderInProcess::InitializeCapabilities() { - capabilities_.gpu = WebContext3DImpl()->GetImplementation()->capabilities(); + capabilities_.gpu = context3d_->GetImplementation()->capabilities(); - size_t mapped_memory_limit = WebContext3DImpl()->GetMappedMemoryLimit(); + size_t mapped_memory_limit = context3d_->GetMappedMemoryLimit(); capabilities_.max_transfer_buffer_usage_bytes = mapped_memory_limit == WebGraphicsContext3DInProcessCommandBufferImpl::kNoLimit @@ -129,21 +115,21 @@ ContextProviderInProcess::ContextCapabilities() { } ::gpu::gles2::GLES2Interface* ContextProviderInProcess::ContextGL() { - DCHECK(WebContext3DImpl()); + DCHECK(context3d_); DCHECK(lost_context_callback_proxy_); // Is bound to thread. DCHECK(context_thread_checker_.CalledOnValidThread()); - return WebContext3DImpl()->GetGLInterface(); + return context3d_->GetGLInterface(); } ::gpu::ContextSupport* ContextProviderInProcess::ContextSupport() { - DCHECK(WebContext3DImpl()); + DCHECK(context3d_); if (!lost_context_callback_proxy_) return NULL; // Not bound to anything. DCHECK(context_thread_checker_.CalledOnValidThread()); - return WebContext3DImpl()->GetContextSupport(); + return context3d_->GetContextSupport(); } class GrContext* ContextProviderInProcess::GrContext() { @@ -153,7 +139,8 @@ class GrContext* ContextProviderInProcess::GrContext() { if (gr_context_) return gr_context_->get(); - gr_context_.reset(new GrContextForWebGraphicsContext3D(gr_interface_)); + gr_context_.reset( + new GrContextForGLES2Interface(context3d_->GetGLInterface())); return gr_context_->get(); } @@ -166,7 +153,7 @@ void ContextProviderInProcess::InvalidateGrContext(uint32_t state) { } void ContextProviderInProcess::SetupLock() { - WebContext3DImpl()->SetLock(&context_lock_); + context3d_->SetLock(&context_lock_); } base::Lock* ContextProviderInProcess::GetLock() { diff --git a/chromium/content/browser/android/in_process/context_provider_in_process.h b/chromium/content/browser/android/in_process/context_provider_in_process.h index fe24f945003..37f494afcb1 100644 --- a/chromium/content/browser/android/in_process/context_provider_in_process.h +++ b/chromium/content/browser/android/in_process/context_provider_in_process.h @@ -14,7 +14,6 @@ #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" #include "cc/blink/context_provider_web_context.h" -#include "skia/ext/refptr.h" namespace blink { class WebGraphicsContext3D; } @@ -24,8 +23,7 @@ class WebGraphicsContext3DInProcessCommandBufferImpl; namespace content { -class GrContextForWebGraphicsContext3D; -class GrGLInterfaceForWebGraphicsContext3D; +class GrContextForGLES2Interface; class ContextProviderInProcess : NON_EXPORTED_BASE(public cc_blink::ContextProviderWebContext) { @@ -45,8 +43,6 @@ class ContextProviderInProcess // cc_blink::ContextProviderWebContext: blink::WebGraphicsContext3D* WebContext3D() override; - gpu_blink::WebGraphicsContext3DInProcessCommandBufferImpl* WebContext3DImpl(); - // cc::ContextProvider: bool BindToCurrentThread() override; void DetachFromThread() override; @@ -67,8 +63,9 @@ class ContextProviderInProcess base::ThreadChecker main_thread_checker_; base::ThreadChecker context_thread_checker_; - skia::RefPtr gr_interface_; - scoped_ptr gr_context_; + scoped_ptr + context3d_; + scoped_ptr gr_context_; LostContextCallback lost_context_callback_; diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc index 628bfccf6a2..4e5f3f132ea 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc +++ b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.cc @@ -17,7 +17,6 @@ #include "content/browser/android/in_process/synchronous_compositor_registry_in_proc.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" @@ -29,6 +28,7 @@ #include "gpu/command_buffer/client/gl_in_process_context.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "ui/gl/android/surface_texture.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_surface_stub.h" @@ -46,30 +46,15 @@ struct ContextHolder { gpu::GLInProcessContext* gl_in_process_context; }; -blink::WebGraphicsContext3D::Attributes GetDefaultAttribs() { - blink::WebGraphicsContext3D::Attributes attributes; - attributes.antialias = false; - attributes.depth = false; - attributes.stencil = false; - attributes.shareResources = true; - attributes.noAutomaticFlushes = true; - - return attributes; -} - ContextHolder CreateContextHolder( - const blink::WebGraphicsContext3D::Attributes& attributes, + const gpu::gles2::ContextCreationAttribHelper& attributes, scoped_refptr service, - const gpu::GLInProcessContextSharedMemoryLimits& mem_limits, - bool is_offscreen) { - gpu::gles2::ContextCreationAttribHelper in_process_attribs; - WebGraphicsContext3DImpl::ConvertAttributes(attributes, &in_process_attribs); - in_process_attribs.lose_context_when_out_of_memory = true; - + const gpu::GLInProcessContextSharedMemoryLimits& mem_limits) { + bool is_offscreen = true; scoped_ptr context(gpu::GLInProcessContext::Create( - service, NULL /* surface */, is_offscreen, gfx::kNullAcceleratedWidget, - gfx::Size(1, 1), NULL /* share_context */, attributes.shareResources, - in_process_attribs, gfx::PreferDiscreteGpu, mem_limits, + service, nullptr /* surface */, is_offscreen, gfx::kNullAcceleratedWidget, + gfx::Size(1, 1), nullptr /* share_context */, attributes, + gfx::PreferDiscreteGpu, mem_limits, BrowserGpuMemoryBufferManager::current(), nullptr)); gpu::GLInProcessContext* context_ptr = context.get(); @@ -86,12 +71,58 @@ ContextHolder CreateContextHolder( } // namespace -class SynchronousCompositorFactoryImpl::VideoContextProvider +SynchronousCompositorFactoryImpl::SynchronousCompositorFactoryImpl() { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { + // TODO(boliu): Figure out how to deal with this more nicely. + SynchronousCompositorFactory::SetInstance(this); + } +} + +SynchronousCompositorFactoryImpl::~SynchronousCompositorFactoryImpl() {} + +scoped_refptr +SynchronousCompositorFactoryImpl::GetCompositorTaskRunner() { + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); +} + +scoped_ptr +SynchronousCompositorFactoryImpl::CreateOutputSurface( + int routing_id, + uint32_t output_surface_id, + const scoped_refptr& frame_swap_message_queue, + const scoped_refptr& onscreen_context, + const scoped_refptr& worker_context) { + return make_scoped_ptr(new SynchronousCompositorOutputSurface( + onscreen_context, worker_context, routing_id, output_surface_id, + SynchronousCompositorRegistryInProc::GetInstance(), + frame_swap_message_queue)); +} + +InputHandlerManagerClient* +SynchronousCompositorFactoryImpl::GetInputHandlerManagerClient() { + return synchronous_input_event_filter(); +} + +SynchronousInputHandlerProxyClient* +SynchronousCompositorFactoryImpl::GetSynchronousInputHandlerProxyClient() { + return synchronous_input_event_filter(); +} + +scoped_ptr +SynchronousCompositorFactoryImpl::CreateExternalBeginFrameSource( + int routing_id) { + return make_scoped_ptr(new SynchronousCompositorExternalBeginFrameSource( + routing_id, SynchronousCompositorRegistryInProc::GetInstance())); +} + +// SynchronousCompositorStreamTextureFactoryImpl. + +class SynchronousCompositorStreamTextureFactoryImpl::VideoContextProvider : public StreamTextureFactorySynchronousImpl::ContextProvider { public: - VideoContextProvider( - scoped_refptr context_provider, - gpu::GLInProcessContext* gl_in_process_context) + VideoContextProvider(scoped_refptr context_provider, + gpu::GLInProcessContext* gl_in_process_context) : context_provider_(context_provider), gl_in_process_context_(gl_in_process_context) { context_provider_->BindToCurrentThread(); @@ -119,8 +150,7 @@ class SynchronousCompositorFactoryImpl::VideoContextProvider } void RestoreContext() { - FOR_EACH_OBSERVER(StreamTextureFactoryContextObserver, - observer_list_, + FOR_EACH_OBSERVER(StreamTextureFactoryContextObserver, observer_list_, ResetStreamTextureProxy()); } @@ -135,87 +165,62 @@ class SynchronousCompositorFactoryImpl::VideoContextProvider DISALLOW_COPY_AND_ASSIGN(VideoContextProvider); }; -SynchronousCompositorFactoryImpl::SynchronousCompositorFactoryImpl() - : num_hardware_compositors_(0) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kSingleProcess)) { - // TODO(boliu): Figure out how to deal with this more nicely. - SynchronousCompositorFactory::SetInstance(this); - } -} - -SynchronousCompositorFactoryImpl::~SynchronousCompositorFactoryImpl() {} - -scoped_refptr -SynchronousCompositorFactoryImpl::GetCompositorTaskRunner() { - return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); -} - -scoped_ptr -SynchronousCompositorFactoryImpl::CreateOutputSurface( - int routing_id, - const scoped_refptr& frame_swap_message_queue, - const scoped_refptr& onscreen_context, - const scoped_refptr& worker_context) { - return make_scoped_ptr(new SynchronousCompositorOutputSurface( - onscreen_context, worker_context, routing_id, - SynchronousCompositorRegistryInProc::GetInstance(), - frame_swap_message_queue)); -} +namespace { +base::LazyInstance::Leaky + g_stream_texture_factory = LAZY_INSTANCE_INITIALIZER; +} // namespace -InputHandlerManagerClient* -SynchronousCompositorFactoryImpl::GetInputHandlerManagerClient() { - return synchronous_input_event_filter(); +// static +SynchronousCompositorStreamTextureFactoryImpl* +SynchronousCompositorStreamTextureFactoryImpl::GetInstance() { + return g_stream_texture_factory.Pointer(); } -scoped_ptr -SynchronousCompositorFactoryImpl::CreateExternalBeginFrameSource( - int routing_id) { - return make_scoped_ptr(new SynchronousCompositorExternalBeginFrameSource( - routing_id, SynchronousCompositorRegistryInProc::GetInstance())); -} +SynchronousCompositorStreamTextureFactoryImpl:: + SynchronousCompositorStreamTextureFactoryImpl() + : num_hardware_compositors_(0) {} scoped_refptr -SynchronousCompositorFactoryImpl::CreateStreamTextureFactory(int frame_id) { - scoped_refptr factory( - StreamTextureFactorySynchronousImpl::Create( - base::Bind( - &SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory, - base::Unretained(this)), - frame_id)); - return factory; +SynchronousCompositorStreamTextureFactoryImpl::CreateStreamTextureFactory() { + return StreamTextureFactorySynchronousImpl::Create( + base::Bind(&SynchronousCompositorStreamTextureFactoryImpl:: + TryCreateStreamTextureFactory, + base::Unretained(this))); } -void SynchronousCompositorFactoryImpl::CompositorInitializedHardwareDraw() { +void SynchronousCompositorStreamTextureFactoryImpl:: + CompositorInitializedHardwareDraw() { base::AutoLock lock(num_hardware_compositor_lock_); num_hardware_compositors_++; if (num_hardware_compositors_ == 1 && main_thread_task_runner_.get()) { main_thread_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &SynchronousCompositorFactoryImpl::RestoreContextOnMainThread, - base::Unretained(this))); + FROM_HERE, base::Bind(&SynchronousCompositorStreamTextureFactoryImpl:: + RestoreContextOnMainThread, + base::Unretained(this))); } } -void SynchronousCompositorFactoryImpl::CompositorReleasedHardwareDraw() { +void SynchronousCompositorStreamTextureFactoryImpl:: + CompositorReleasedHardwareDraw() { base::AutoLock lock(num_hardware_compositor_lock_); DCHECK_GT(num_hardware_compositors_, 0u); num_hardware_compositors_--; } -void SynchronousCompositorFactoryImpl::RestoreContextOnMainThread() { +void SynchronousCompositorStreamTextureFactoryImpl:: + RestoreContextOnMainThread() { if (CanCreateMainThreadContext() && video_context_provider_.get()) video_context_provider_->RestoreContext(); } -bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() { +bool SynchronousCompositorStreamTextureFactoryImpl:: + CanCreateMainThreadContext() { base::AutoLock lock(num_hardware_compositor_lock_); return num_hardware_compositors_ > 0; } scoped_refptr -SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() { +SynchronousCompositorStreamTextureFactoryImpl::TryCreateStreamTextureFactory() { { base::AutoLock lock(num_hardware_compositor_lock_); main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get(); @@ -226,20 +231,29 @@ SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() { // |video_context_provider_| to null is also not safe since it makes // synchronous destruction uncontrolled and possibly deadlock. if (!CanCreateMainThreadContext()) { - return - scoped_refptr(); + return scoped_refptr< + StreamTextureFactorySynchronousImpl::ContextProvider>(); } if (!video_context_provider_.get()) { DCHECK(android_view_service_.get()); - blink::WebGraphicsContext3D::Attributes attributes = GetDefaultAttribs(); - attributes.shareResources = false; + // This is for an offscreen context, so the default framebuffer doesn't need + // any alpha, depth, stencil, antialiasing. + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 0; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.bind_generates_resource = false; + attributes.lose_context_when_out_of_memory = true; + // This needs to run in on-screen |android_view_service_| context due to // SurfaceTexture limitations. ContextHolder holder = CreateContextHolder(attributes, android_view_service_, - gpu::GLInProcessContextSharedMemoryLimits(), false); + gpu::GLInProcessContextSharedMemoryLimits()); video_context_provider_ = new VideoContextProvider( ContextProviderInProcess::Create(std::move(holder.command_buffer), "Video-Offscreen-main-thread"), @@ -248,10 +262,15 @@ SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() { return video_context_provider_; } -void SynchronousCompositorFactoryImpl::SetDeferredGpuService( +void SynchronousCompositorStreamTextureFactoryImpl::SetDeferredGpuService( scoped_refptr service) { DCHECK(!android_view_service_.get()); android_view_service_ = service; + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { + RenderThreadImpl::SetStreamTextureFactory(CreateStreamTextureFactory()); + } } } // namespace content diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h index 03533444a3e..4842e85f30e 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h +++ b/chromium/content/browser/android/in_process/synchronous_compositor_factory_impl.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_FACTORY_IMPL_H_ #define CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_FACTORY_IMPL_H_ +#include "base/lazy_instance.h" #include "base/synchronization/lock.h" #include "cc/blink/context_provider_web_context.h" #include "content/browser/android/in_process/synchronous_input_event_filter.h" @@ -40,34 +41,46 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { override; scoped_ptr CreateOutputSurface( int routing_id, + uint32_t output_surface_id, const scoped_refptr& frame_swap_message_queue, const scoped_refptr& onscreen_context, const scoped_refptr& worker_context) override; InputHandlerManagerClient* GetInputHandlerManagerClient() override; + SynchronousInputHandlerProxyClient* GetSynchronousInputHandlerProxyClient() + override; scoped_ptr CreateExternalBeginFrameSource( int routing_id) override; - scoped_refptr CreateStreamTextureFactory( - int frame_id) override; SynchronousInputEventFilter* synchronous_input_event_filter() { return &synchronous_input_event_filter_; } + private: + SynchronousInputEventFilter synchronous_input_event_filter_; +}; + +class SynchronousCompositorStreamTextureFactoryImpl { + public: + static SynchronousCompositorStreamTextureFactoryImpl* GetInstance(); + + scoped_refptr CreateStreamTextureFactory(); void SetDeferredGpuService( scoped_refptr service); void CompositorInitializedHardwareDraw(); void CompositorReleasedHardwareDraw(); - private: - scoped_refptr GetSharedWorkerContextProvider(); + friend struct base::DefaultLazyInstanceTraits< + SynchronousCompositorStreamTextureFactoryImpl>; + + SynchronousCompositorStreamTextureFactoryImpl(); + ~SynchronousCompositorStreamTextureFactoryImpl(); + bool CanCreateMainThreadContext(); scoped_refptr TryCreateStreamTextureFactory(); void RestoreContextOnMainThread(); - SynchronousInputEventFilter synchronous_input_event_filter_; - scoped_refptr android_view_service_; class VideoContextProvider; diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc b/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc index e1791374ed2..f1829890a09 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc +++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.cc @@ -108,12 +108,6 @@ void SynchronousCompositorImpl::RegisterWithClient() { synchronous_input_handler_proxy_->SetOnlySynchronouslyAnimateRootFlings(this); } -// static -void SynchronousCompositorImpl::SetGpuServiceInProc( - scoped_refptr service) { - g_factory.Get().SetDeferredGpuService(service); -} - void SynchronousCompositorImpl::DidInitializeRendererObjects( SynchronousCompositorOutputSurface* output_surface, SynchronousCompositorExternalBeginFrameSource* begin_frame_source, @@ -156,7 +150,7 @@ void SynchronousCompositorImpl::DidDestroyRendererObjects() { need_animate_input_ = false; } -scoped_ptr SynchronousCompositorImpl::DemandDrawHw( +SynchronousCompositor::Frame SynchronousCompositorImpl::DemandDrawHw( const gfx::Size& surface_size, const gfx::Transform& transform, const gfx::Rect& viewport, @@ -166,45 +160,48 @@ scoped_ptr SynchronousCompositorImpl::DemandDrawHw( DCHECK(CalledOnValidThread()); DCHECK(output_surface_); DCHECK(begin_frame_source_); - DCHECK(!frame_holder_); + DCHECK(!frame_holder_.frame); output_surface_->DemandDrawHw(surface_size, transform, viewport, clip, viewport_rect_for_tile_priority, transform_for_tile_priority); - if (frame_holder_) - UpdateFrameMetaData(frame_holder_->metadata); + if (frame_holder_.frame) + UpdateFrameMetaData(frame_holder_.frame->metadata); return std::move(frame_holder_); } void SynchronousCompositorImpl::ReturnResources( + uint32_t output_surface_id, const cc::CompositorFrameAck& frame_ack) { DCHECK(CalledOnValidThread()); - output_surface_->ReturnResources(frame_ack); + output_surface_->ReturnResources(output_surface_id, frame_ack); } bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) { DCHECK(CalledOnValidThread()); DCHECK(output_surface_); DCHECK(begin_frame_source_); - DCHECK(!frame_holder_); + DCHECK(!frame_holder_.frame); output_surface_->DemandDrawSw(canvas); - bool success = !!frame_holder_; - if (frame_holder_) { - UpdateFrameMetaData(frame_holder_->metadata); - frame_holder_.reset(); + bool success = !!frame_holder_.frame; + if (frame_holder_.frame) { + UpdateFrameMetaData(frame_holder_.frame->metadata); + frame_holder_.frame.reset(); } return success; } -void SynchronousCompositorImpl::SwapBuffers(cc::CompositorFrame* frame) { - DCHECK(!frame_holder_); - frame_holder_.reset(new cc::CompositorFrame); - frame->AssignTo(frame_holder_.get()); +void SynchronousCompositorImpl::SwapBuffers(uint32_t output_surface_id, + cc::CompositorFrame* frame) { + DCHECK(!frame_holder_.frame); + frame_holder_.output_surface_id = output_surface_id; + frame_holder_.frame.reset(new cc::CompositorFrame); + frame->AssignTo(frame_holder_.frame.get()); } void SynchronousCompositorImpl::UpdateFrameMetaData( @@ -221,9 +218,11 @@ void SynchronousCompositorImpl::SetMemoryPolicy(size_t bytes_limit) { output_surface_->SetMemoryPolicy(bytes_limit); if (bytes_limit && !current_bytes_limit) { - g_factory.Get().CompositorInitializedHardwareDraw(); + SynchronousCompositorStreamTextureFactoryImpl::GetInstance() + ->CompositorInitializedHardwareDraw(); } else if (!bytes_limit && current_bytes_limit) { - g_factory.Get().CompositorReleasedHardwareDraw(); + SynchronousCompositorStreamTextureFactoryImpl::GetInstance() + ->CompositorReleasedHardwareDraw(); } } @@ -242,6 +241,14 @@ void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset( root_offset); } +void SynchronousCompositorImpl::SynchronouslyZoomBy(float zoom_delta, + const gfx::Point& anchor) { + DCHECK(CalledOnValidThread()); + if (!synchronous_input_handler_proxy_) + return; + synchronous_input_handler_proxy_->SynchronouslyZoomBy(zoom_delta, anchor); +} + void SynchronousCompositorImpl::SetIsActive(bool is_active) { TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active", is_active); @@ -283,7 +290,7 @@ void SynchronousCompositorImpl::UpdateNeedsBeginFrames() { rwhva_->OnSetNeedsBeginFrames(is_active_ && renderer_needs_begin_frames_); } -void SynchronousCompositorImpl::DidOverscroll( +void SynchronousCompositorImpl::DidOverscrollInProcess( const DidOverscrollParams& params) { if (registered_with_client_) { compositor_client_->DidOverscroll(params.accumulated_overscroll, @@ -307,6 +314,13 @@ InputEventAckState SynchronousCompositorImpl::HandleInputEvent( routing_id_, input_event); } +void SynchronousCompositorImpl::DidOverscroll( + const DidOverscrollParams& params) { + // SynchronousCompositorImpl uses synchronous DidOverscrollInProcess for + // overscroll instead of this async path. + NOTREACHED(); +} + bool SynchronousCompositorImpl::OnMessageReceived(const IPC::Message& message) { NOTREACHED(); return false; diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_impl.h b/chromium/content/browser/android/in_process/synchronous_compositor_impl.h index e5a14611d8c..29f48d2a707 100644 --- a/chromium/content/browser/android/in_process/synchronous_compositor_impl.h +++ b/chromium/content/browser/android/in_process/synchronous_compositor_impl.h @@ -43,9 +43,6 @@ class SynchronousCompositorImpl // is implicitly that of the in-process renderer. static SynchronousCompositorImpl* FromRoutingID(int routing_id); - static void SetGpuServiceInProc( - scoped_refptr service); - ~SynchronousCompositorImpl() override; // Called by SynchronousCompositorRegistry. @@ -60,10 +57,11 @@ class SynchronousCompositorImpl // SynchronousCompositorOutputSurfaceClient overrides. void Invalidate() override; - void SwapBuffers(cc::CompositorFrame* frame) override; + void SwapBuffers(uint32_t output_surface_id, + cc::CompositorFrame* frame) override; // SynchronousCompositor overrides. - scoped_ptr DemandDrawHw( + SynchronousCompositor::Frame DemandDrawHw( const gfx::Size& surface_size, const gfx::Transform& transform, const gfx::Rect& viewport, @@ -71,10 +69,12 @@ class SynchronousCompositorImpl const gfx::Rect& viewport_rect_for_tile_priority, const gfx::Transform& transform_for_tile_priority) override; bool DemandDrawSw(SkCanvas* canvas) override; - void ReturnResources(const cc::CompositorFrameAck& frame_ack) override; + void ReturnResources(uint32_t output_surface_id, + const cc::CompositorFrameAck& frame_ack) override; void SetMemoryPolicy(size_t bytes_limit) override; void DidChangeRootLayerScrollOffset( const gfx::ScrollOffset& root_offset) override; + void SynchronouslyZoomBy(float zoom_delta, const gfx::Point& anchor) override; void SetIsActive(bool is_active) override; void OnComputeScroll(base::TimeTicks animation_time) override; @@ -82,6 +82,7 @@ class SynchronousCompositorImpl void BeginFrame(const cc::BeginFrameArgs& args) override; InputEventAckState HandleInputEvent( const blink::WebInputEvent& input_event) override; + void DidOverscroll(const DidOverscrollParams& params) override; bool OnMessageReceived(const IPC::Message& message) override; void DidBecomeCurrent() override; @@ -94,7 +95,7 @@ class SynchronousCompositorImpl float min_page_scale_factor, float max_page_scale_factor) override; - void DidOverscroll(const DidOverscrollParams& params); + void DidOverscrollInProcess(const DidOverscrollParams& params); void DidStopFlinging(); private: @@ -118,7 +119,7 @@ class SynchronousCompositorImpl bool is_active_; bool renderer_needs_begin_frames_; bool need_animate_input_; - scoped_ptr frame_holder_; + SynchronousCompositor::Frame frame_holder_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.cc b/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.cc new file mode 100644 index 00000000000..38056fbd7ac --- /dev/null +++ b/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.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 "content/browser/android/in_process/synchronous_compositor_renderer_statics.h" + +#include "content/renderer/android/synchronous_compositor_proxy.h" + +namespace content { + +void SynchronousCompositorSetSkCanvas(SkCanvas* canvas) { + SynchronousCompositorProxy::SetSkCanvasForDraw(canvas); +} + +} // namespace content diff --git a/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.h b/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.h new file mode 100644 index 00000000000..80823671758 --- /dev/null +++ b/chromium/content/browser/android/in_process/synchronous_compositor_renderer_statics.h @@ -0,0 +1,16 @@ +// 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 CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_RENDERER_STATICS_H_ +#define CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_RENDERER_STATICS_H_ + +class SkCanvas; + +namespace content { + +void SynchronousCompositorSetSkCanvas(SkCanvas* canvas); + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_IN_PROCESS_SYNCHRONOUS_COMPOSITOR_RENDERER_STATICS_H_ diff --git a/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc b/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc index 3ce1c61eb4f..b4d9f7fa82b 100644 --- a/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc +++ b/chromium/content/browser/android/in_process/synchronous_input_event_filter.cc @@ -40,26 +40,15 @@ void SynchronousInputEventFilter::SetBoundHandler(const Handler& handler) { base::Unretained(this), handler)); } +void SynchronousInputEventFilter::DidAddInputHandler(int routing_id) {} +void SynchronousInputEventFilter::DidRemoveInputHandler(int routing_id) {} + void SynchronousInputEventFilter::SetBoundHandlerOnUIThread( const Handler& handler) { DCHECK_CURRENTLY_ON(BrowserThread::UI); handler_ = handler; } -void SynchronousInputEventFilter::DidAddInputHandler( - int routing_id, - ui::SynchronousInputHandlerProxy* synchronous_input_handler_proxy) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - SynchronousCompositorRegistryInProc::GetInstance()->RegisterInputHandler( - routing_id, synchronous_input_handler_proxy); -} - -void SynchronousInputEventFilter::DidRemoveInputHandler(int routing_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - SynchronousCompositorRegistryInProc::GetInstance()->UnregisterInputHandler( - routing_id); -} - void SynchronousInputEventFilter::DidOverscroll( int routing_id, const DidOverscrollParams& params) { @@ -68,7 +57,7 @@ void SynchronousInputEventFilter::DidOverscroll( SynchronousCompositorImpl* compositor = SynchronousCompositorImpl::FromRoutingID(routing_id); if (compositor) - compositor->DidOverscroll(params); + compositor->DidOverscrollInProcess(params); } void SynchronousInputEventFilter::DidStopFlinging(int routing_id) { @@ -80,4 +69,23 @@ void SynchronousInputEventFilter::DidStopFlinging(int routing_id) { compositor->DidStopFlinging(); } +void SynchronousInputEventFilter::NotifyInputEventHandled( + int routing_id, + blink::WebInputEvent::Type type) {} + +void SynchronousInputEventFilter::DidAddSynchronousHandlerProxy( + int routing_id, + ui::SynchronousInputHandlerProxy* synchronous_input_handler_proxy) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + SynchronousCompositorRegistryInProc::GetInstance()->RegisterInputHandler( + routing_id, synchronous_input_handler_proxy); +} + +void SynchronousInputEventFilter::DidRemoveSynchronousHandlerProxy( + int routing_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + SynchronousCompositorRegistryInProc::GetInstance()->UnregisterInputHandler( + routing_id); +} + } // namespace content diff --git a/chromium/content/browser/android/in_process/synchronous_input_event_filter.h b/chromium/content/browser/android/in_process/synchronous_input_event_filter.h index 98375d73bf8..9ecb400ff02 100644 --- a/chromium/content/browser/android/in_process/synchronous_input_event_filter.h +++ b/chromium/content/browser/android/in_process/synchronous_input_event_filter.h @@ -26,7 +26,9 @@ namespace content { // The provided |handler| process WebInputEvents synchronously on the merged // UI and compositing thread. If the event goes unhandled, that is reflected in // the InputEventAckState; no forwarding is performed. -class SynchronousInputEventFilter : public InputHandlerManagerClient { +class SynchronousInputEventFilter + : public InputHandlerManagerClient, + public SynchronousInputHandlerProxyClient { public: SynchronousInputEventFilter(); ~SynchronousInputEventFilter() override; @@ -36,14 +38,20 @@ class SynchronousInputEventFilter : public InputHandlerManagerClient { // InputHandlerManagerClient implementation. void SetBoundHandler(const Handler& handler) override; - void DidAddInputHandler( - int routing_id, - ui::SynchronousInputHandlerProxy* - synchronous_input_handler_proxy) override; + void DidAddInputHandler(int routing_id) override; void DidRemoveInputHandler(int routing_id) override; void DidOverscroll(int routing_id, const DidOverscrollParams& params) override; void DidStopFlinging(int routing_id) override; + void NotifyInputEventHandled(int routing_id, + blink::WebInputEvent::Type type) override; + + // SynchronousInputHandlerProxyClient overrides. + void DidAddSynchronousHandlerProxy( + int routing_id, + ui::SynchronousInputHandlerProxy* synchronous_input_handler_proxy) + override; + void DidRemoveSynchronousHandlerProxy(int routing_id) override; private: void SetBoundHandlerOnUIThread(const Handler& handler); diff --git a/chromium/content/browser/android/in_process_surface_texture_manager.h b/chromium/content/browser/android/in_process_surface_texture_manager.h index 24e951c7b2f..8cf4d2eebdd 100644 --- a/chromium/content/browser/android/in_process_surface_texture_manager.h +++ b/chromium/content/browser/android/in_process_surface_texture_manager.h @@ -5,22 +5,22 @@ #ifndef CONTENT_BROWSER_ANDROID_IN_PROCESS_SURFACE_TEXTURE_MANAGER_H_ #define CONTENT_BROWSER_ANDROID_IN_PROCESS_SURFACE_TEXTURE_MANAGER_H_ -#include "content/common/android/surface_texture_manager.h" +#include "gpu/ipc/common/android/surface_texture_manager.h" #include "base/containers/scoped_ptr_hash_map.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/synchronization/lock.h" -#include "content/common/android/surface_texture_peer.h" #include "content/common/content_export.h" +#include "gpu/ipc/common/android/surface_texture_peer.h" #include "ui/gl/android/scoped_java_surface.h" namespace content { class CONTENT_EXPORT InProcessSurfaceTextureManager - : public SurfaceTextureManager, - public SurfaceTexturePeer { + : public gpu::SurfaceTextureManager, + public gpu::SurfaceTexturePeer { public: static InProcessSurfaceTextureManager* GetInstance(); diff --git a/chromium/content/browser/android/java/OWNERS b/chromium/content/browser/android/java/OWNERS index 0cebb68c10c..ce79cad30e2 100644 --- a/chromium/content/browser/android/java/OWNERS +++ b/chromium/content/browser/android/java/OWNERS @@ -1,2 +1,2 @@ -mnaganov@chromium.org +michaelbai@chromium.org torne@chromium.org diff --git a/chromium/content/browser/android/service_registry_android_impl.cc b/chromium/content/browser/android/service_registry_android_impl.cc new file mode 100644 index 00000000000..e54c3610a29 --- /dev/null +++ b/chromium/content/browser/android/service_registry_android_impl.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 "content/browser/android/service_registry_android_impl.h" + +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/callback.h" +#include "base/memory/ptr_util.h" +#include "content/public/common/service_registry.h" +#include "jni/ServiceRegistry_jni.h" +#include "mojo/public/cpp/system/message_pipe.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF8; +using base::android::ScopedJavaGlobalRef; + +namespace content { + +namespace { + +// Callback passed to the wrapped ServiceRegistry upon AddService(). The +// ServiceRegistry will call it to create a registered Java service +void CreateImplAndAttach( + const ScopedJavaGlobalRef& j_scoped_service_registry, + const ScopedJavaGlobalRef& j_scoped_manager, + const ScopedJavaGlobalRef& j_scoped_factory, + mojo::ScopedMessagePipeHandle handle) { + JNIEnv* env = AttachCurrentThread(); + Java_ServiceRegistry_createImplAndAttach( + env, j_scoped_service_registry.obj(), handle.release().value(), + j_scoped_manager.obj(), j_scoped_factory.obj()); +} + +} // namespace + +// static +std::unique_ptr ServiceRegistryAndroid::Create( + ServiceRegistry* registry) { + return base::WrapUnique(new ServiceRegistryAndroidImpl(registry)); +} + +// static +bool ServiceRegistryAndroidImpl::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +ServiceRegistryAndroidImpl::~ServiceRegistryAndroidImpl() { + Java_ServiceRegistry_destroy(AttachCurrentThread(), obj_.obj()); +} + +// Constructor and destructor call into Java. +ServiceRegistryAndroidImpl::ServiceRegistryAndroidImpl( + ServiceRegistry* service_registry) + : service_registry_(service_registry) { + JNIEnv* env = AttachCurrentThread(); + obj_.Reset( + env, + Java_ServiceRegistry_create(env, reinterpret_cast(this)).obj()); +} + +const base::android::ScopedJavaGlobalRef& +ServiceRegistryAndroidImpl::GetObj() { + return obj_; +} + +// Methods called from Java. +void ServiceRegistryAndroidImpl::AddService( + JNIEnv* env, + const JavaParamRef& j_service_registry, + const JavaParamRef& j_manager, + const JavaParamRef& j_factory, + const JavaParamRef& j_name) { + std::string name(ConvertJavaStringToUTF8(env, j_name)); + + ScopedJavaGlobalRef j_scoped_service_registry; + j_scoped_service_registry.Reset(env, j_service_registry); + + ScopedJavaGlobalRef j_scoped_manager; + j_scoped_manager.Reset(env, j_manager); + + ScopedJavaGlobalRef j_scoped_factory; + j_scoped_factory.Reset(env, j_factory); + + service_registry_->AddService( + name, base::Bind(&CreateImplAndAttach, j_scoped_service_registry, + j_scoped_manager, j_scoped_factory)); +} + +void ServiceRegistryAndroidImpl::RemoveService( + JNIEnv* env, + const JavaParamRef& j_service_registry, + const JavaParamRef& j_name) { + std::string name(ConvertJavaStringToUTF8(env, j_name)); + service_registry_->RemoveService(name); +} + +void ServiceRegistryAndroidImpl::ConnectToRemoteService( + JNIEnv* env, + const JavaParamRef& j_service_registry, + const JavaParamRef& j_name, + jint j_handle) { + std::string name(ConvertJavaStringToUTF8(env, j_name)); + mojo::ScopedMessagePipeHandle handle((mojo::MessagePipeHandle(j_handle))); + service_registry_->ConnectToRemoteService(name, std::move(handle)); +} + +} // namespace content diff --git a/chromium/content/browser/android/service_registry_android_impl.h b/chromium/content/browser/android/service_registry_android_impl.h new file mode 100644 index 00000000000..2620313c801 --- /dev/null +++ b/chromium/content/browser/android/service_registry_android_impl.h @@ -0,0 +1,53 @@ +// 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 CONTENT_BROWSER_ANDROID_SERVICE_REGISTRY_ANDROID_IMPL_H_ +#define CONTENT_BROWSER_ANDROID_SERVICE_REGISTRY_ANDROID_IMPL_H_ + +#include + +#include "base/macros.h" +#include "content/public/browser/android/service_registry_android.h" + +namespace content { + +class ServiceRegistryAndroidImpl : public ServiceRegistryAndroid { + public: + static bool Register(JNIEnv* env); + + ~ServiceRegistryAndroidImpl() override; + + private: + friend class ServiceRegistryAndroid; + + // Use ServiceRegistryAndroid::Create() to create an instance. + explicit ServiceRegistryAndroidImpl(ServiceRegistry* service_registry); + + // ServiceRegistryAndroid implementation: + void AddService( + JNIEnv* env, + const base::android::JavaParamRef& j_service_registry, + const base::android::JavaParamRef& j_manager, + const base::android::JavaParamRef& j_factory, + const base::android::JavaParamRef& j_name) override; + void RemoveService( + JNIEnv* env, + const base::android::JavaParamRef& j_service_registry, + const base::android::JavaParamRef& j_name) override; + void ConnectToRemoteService( + JNIEnv* env, + const base::android::JavaParamRef& j_service_registry, + const base::android::JavaParamRef& j_name, + jint handle) override; + const base::android::ScopedJavaGlobalRef& GetObj() override; + + ServiceRegistry* service_registry_; + base::android::ScopedJavaGlobalRef obj_; + + DISALLOW_COPY_AND_ASSIGN(ServiceRegistryAndroidImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_SERVICE_REGISTRY_ANDROID_IMPL_H_ diff --git a/chromium/content/browser/android/synchronous_compositor_base.cc b/chromium/content/browser/android/synchronous_compositor_base.cc index acc5e349f99..0d085095b91 100644 --- a/chromium/content/browser/android/synchronous_compositor_base.cc +++ b/chromium/content/browser/android/synchronous_compositor_base.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/supports_user_data.h" +#include "content/browser/android/in_process/synchronous_compositor_factory_impl.h" #include "content/browser/android/in_process/synchronous_compositor_impl.h" #include "content/browser/android/synchronous_compositor_host.h" #include "content/browser/gpu/gpu_process_host.h" @@ -20,27 +21,25 @@ class SynchronousCompositorClient; namespace { -gpu::SyncPointManager* g_sync_point_manager = nullptr; +base::LazyInstance> + g_gpu_service = LAZY_INSTANCE_INITIALIZER; base::Thread* CreateInProcessGpuThreadForSynchronousCompositor( - const InProcessChildThreadParams& params) { - DCHECK(g_sync_point_manager); - return new InProcessGpuThread(params, g_sync_point_manager); + const InProcessChildThreadParams& params, + const gpu::GpuPreferences& gpu_preferences) { + DCHECK(g_gpu_service.Get()); + return new InProcessGpuThread(params, gpu_preferences, + g_gpu_service.Get()->sync_point_manager()); } } // namespace void SynchronousCompositor::SetGpuService( scoped_refptr service) { - DCHECK(!g_sync_point_manager); - g_sync_point_manager = service->sync_point_manager(); + DCHECK(!g_gpu_service.Get()); + g_gpu_service.Get() = service; GpuProcessHost::RegisterGpuMainThreadFactory( CreateInProcessGpuThreadForSynchronousCompositor); - - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kIPCSyncCompositing)) { - SynchronousCompositorImpl::SetGpuServiceInProc(service); - } } // static @@ -65,10 +64,15 @@ scoped_ptr SynchronousCompositorBase::Create( if (!web_contents_android->synchronous_compositor_client()) return nullptr; // Not using sync compositing. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kIPCSyncCompositing)) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kIPCSyncCompositing)) { + bool async_input = + !command_line->HasSwitch(switches::kSyncInputForSyncCompositor); + bool use_in_proc_software_draw = + command_line->HasSwitch(switches::kSingleProcess); return make_scoped_ptr(new SynchronousCompositorHost( - rwhva, web_contents_android->synchronous_compositor_client())); + rwhva, web_contents_android->synchronous_compositor_client(), + async_input, use_in_proc_software_draw)); } return make_scoped_ptr(new SynchronousCompositorImpl( rwhva, web_contents_android->synchronous_compositor_client())); diff --git a/chromium/content/browser/android/synchronous_compositor_base.h b/chromium/content/browser/android/synchronous_compositor_base.h index 6bd41bb24d4..218e82199da 100644 --- a/chromium/content/browser/android/synchronous_compositor_base.h +++ b/chromium/content/browser/android/synchronous_compositor_base.h @@ -24,7 +24,9 @@ struct BeginFrameArgs; namespace content { class RenderWidgetHostViewAndroid; +class SynchronousCompositorStreamTextureFactoryImpl; class WebContents; +struct DidOverscrollParams; class SynchronousCompositorBase : public SynchronousCompositor { public: @@ -37,6 +39,7 @@ class SynchronousCompositorBase : public SynchronousCompositor { virtual void BeginFrame(const cc::BeginFrameArgs& args) = 0; virtual InputEventAckState HandleInputEvent( const blink::WebInputEvent& input_event) = 0; + virtual void DidOverscroll(const DidOverscrollParams& over_scroll_params) = 0; virtual bool OnMessageReceived(const IPC::Message& message) = 0; virtual void DidBecomeCurrent() = 0; diff --git a/chromium/content/browser/android/synchronous_compositor_host.cc b/chromium/content/browser/android/synchronous_compositor_host.cc index 83dd85fead4..7f6cdfc5fb3 100644 --- a/chromium/content/browser/android/synchronous_compositor_host.cc +++ b/chromium/content/browser/android/synchronous_compositor_host.cc @@ -10,6 +10,8 @@ #include "base/memory/shared_memory.h" #include "base/trace_event/trace_event_argument.h" #include "cc/output/compositor_frame_ack.h" +#include "content/browser/android/in_process/synchronous_compositor_factory_impl.h" +#include "content/browser/android/in_process/synchronous_compositor_renderer_statics.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/android/sync_compositor_messages.h" @@ -27,21 +29,26 @@ namespace content { SynchronousCompositorHost::SynchronousCompositorHost( RenderWidgetHostViewAndroid* rwhva, - SynchronousCompositorClient* client) + SynchronousCompositorClient* client, + bool async_input, + bool use_in_proc_software_draw) : rwhva_(rwhva), client_(client), ui_task_runner_( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()), sender_(rwhva_->GetRenderWidgetHost()), + async_input_(async_input), + use_in_process_zero_copy_software_draw_(use_in_proc_software_draw), is_active_(false), bytes_limit_(0u), + output_surface_id_from_last_draw_(0u), root_scroll_offset_updated_by_browser_(false), renderer_param_version_(0u), need_animate_scroll_(false), - need_invalidate_(false), + need_invalidate_count_(0u), need_begin_frame_(false), - did_activate_pending_tree_(false), + did_activate_pending_tree_count_(0u), weak_ptr_factory_(this) { client_->DidInitializeCompositor(this); } @@ -66,7 +73,7 @@ void SynchronousCompositorHost::DidBecomeCurrent() { client_->DidBecomeCurrent(this); } -scoped_ptr SynchronousCompositorHost::DemandDrawHw( +SynchronousCompositor::Frame SynchronousCompositorHost::DemandDrawHw( const gfx::Size& surface_size, const gfx::Transform& transform, const gfx::Rect& viewport, @@ -76,22 +83,27 @@ scoped_ptr SynchronousCompositorHost::DemandDrawHw( SyncCompositorDemandDrawHwParams params(surface_size, transform, viewport, clip, viewport_rect_for_tile_priority, transform_for_tile_priority); - scoped_ptr frame(new cc::CompositorFrame); + SynchronousCompositor::Frame frame; + frame.frame.reset(new cc::CompositorFrame); SyncCompositorCommonBrowserParams common_browser_params; PopulateCommonParams(&common_browser_params); SyncCompositorCommonRendererParams common_renderer_params; if (!sender_->Send(new SyncCompositorMsg_DemandDrawHw( routing_id_, common_browser_params, params, &common_renderer_params, - frame.get()))) { - return nullptr; + &frame.output_surface_id, frame.frame.get()))) { + return SynchronousCompositor::Frame(); } ProcessCommonParams(common_renderer_params); - if (!frame->delegated_frame_data) { + if (!frame.frame->delegated_frame_data) { // This can happen if compositor did not swap in this draw. - frame.reset(); + frame.frame.reset(); + } + if (frame.frame) { + UpdateFrameMetaData(frame.frame->metadata); + if (output_surface_id_from_last_draw_ != frame.output_surface_id) + returned_resources_.clear(); + output_surface_id_from_last_draw_ = frame.output_surface_id; } - if (frame) - UpdateFrameMetaData(frame->metadata); return frame; } @@ -100,6 +112,44 @@ void SynchronousCompositorHost::UpdateFrameMetaData( rwhva_->SynchronousFrameMetadata(frame_metadata); } +namespace { + +class ScopedSetSkCanvas { + public: + explicit ScopedSetSkCanvas(SkCanvas* canvas) { + SynchronousCompositorSetSkCanvas(canvas); + } + + ~ScopedSetSkCanvas() { + SynchronousCompositorSetSkCanvas(nullptr); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedSetSkCanvas); +}; + +} + +bool SynchronousCompositorHost::DemandDrawSwInProc(SkCanvas* canvas) { + SyncCompositorCommonBrowserParams common_browser_params; + PopulateCommonParams(&common_browser_params); + SyncCompositorCommonRendererParams common_renderer_params; + bool success = false; + scoped_ptr frame(new cc::CompositorFrame); + ScopedSetSkCanvas set_sk_canvas(canvas); + SyncCompositorDemandDrawSwParams params; // Unused. + if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( + routing_id_, common_browser_params, params, &success, + &common_renderer_params, frame.get()))) { + return false; + } + if (!success) + return false; + ProcessCommonParams(common_renderer_params); + UpdateFrameMetaData(frame->metadata); + return true; +} + class SynchronousCompositorHost::ScopedSendZeroMemory { public: ScopedSendZeroMemory(SynchronousCompositorHost* host) : host_(host) {} @@ -124,6 +174,9 @@ struct SynchronousCompositorHost::SharedMemoryWithSize { }; bool SynchronousCompositorHost::DemandDrawSw(SkCanvas* canvas) { + if (use_in_process_zero_copy_software_draw_) + return DemandDrawSwInProc(canvas); + SyncCompositorDemandDrawSwParams params; params.size = gfx::Size(canvas->getBaseLayerSize().width(), canvas->getBaseLayerSize().height()); @@ -222,7 +275,13 @@ void SynchronousCompositorHost::SendZeroMemory() { } void SynchronousCompositorHost::ReturnResources( + uint32_t output_surface_id, const cc::CompositorFrameAck& frame_ack) { + // If output_surface_id does not match, then renderer side has switched + // to a new OutputSurface, so dropping resources for old OutputSurface + // is allowed. + if (output_surface_id_from_last_draw_ != output_surface_id) + return; returned_resources_.insert(returned_resources_.end(), frame_ack.resources.begin(), frame_ack.resources.end()); @@ -231,8 +290,17 @@ void SynchronousCompositorHost::ReturnResources( void SynchronousCompositorHost::SetMemoryPolicy(size_t bytes_limit) { if (bytes_limit_ == bytes_limit) return; + size_t current_bytes_limit = bytes_limit_; bytes_limit_ = bytes_limit; SendAsyncCompositorStateIfNeeded(); + + if (bytes_limit && !current_bytes_limit) { + SynchronousCompositorStreamTextureFactoryImpl::GetInstance() + ->CompositorInitializedHardwareDraw(); + } else if (!bytes_limit && current_bytes_limit) { + SynchronousCompositorStreamTextureFactoryImpl::GetInstance() + ->CompositorReleasedHardwareDraw(); + } } void SynchronousCompositorHost::DidChangeRootLayerScrollOffset( @@ -261,6 +329,19 @@ void SynchronousCompositorHost::UpdateStateTask() { DCHECK(!weak_ptr_factory_.HasWeakPtrs()); } +void SynchronousCompositorHost::SynchronouslyZoomBy(float zoom_delta, + const gfx::Point& anchor) { + SyncCompositorCommonBrowserParams common_browser_params; + PopulateCommonParams(&common_browser_params); + SyncCompositorCommonRendererParams common_renderer_params; + if (!sender_->Send(new SyncCompositorMsg_ZoomBy( + routing_id_, common_browser_params, zoom_delta, anchor, + &common_renderer_params))) { + return; + } + ProcessCommonParams(common_renderer_params); +} + void SynchronousCompositorHost::SetIsActive(bool is_active) { if (is_active_ == is_active) return; @@ -278,16 +359,14 @@ void SynchronousCompositorHost::OnComputeScroll( SyncCompositorCommonBrowserParams common_browser_params; PopulateCommonParams(&common_browser_params); SyncCompositorCommonRendererParams common_renderer_params; - if (!sender_->Send(new SyncCompositorMsg_ComputeScroll( - routing_id_, common_browser_params, animation_time, - &common_renderer_params))) { - return; - } - ProcessCommonParams(common_renderer_params); + sender_->Send(new SyncCompositorMsg_ComputeScroll( + routing_id_, common_browser_params, animation_time)); } InputEventAckState SynchronousCompositorHost::HandleInputEvent( const blink::WebInputEvent& input_event) { + if (async_input_) + return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; SyncCompositorCommonBrowserParams common_browser_params; PopulateCommonParams(&common_browser_params); SyncCompositorCommonRendererParams common_renderer_params; @@ -301,8 +380,15 @@ InputEventAckState SynchronousCompositorHost::HandleInputEvent( return ack; } +void SynchronousCompositorHost::DidOverscroll( + const DidOverscrollParams& over_scroll_params) { + client_->DidOverscroll(over_scroll_params.accumulated_overscroll, + over_scroll_params.latest_overscroll_delta, + over_scroll_params.current_fling_velocity); +} + void SynchronousCompositorHost::BeginFrame(const cc::BeginFrameArgs& args) { - if (!is_active_ || !need_begin_frame_) + if (!is_active_) return; SyncCompositorCommonBrowserParams common_browser_params; @@ -320,9 +406,7 @@ void SynchronousCompositorHost::OnOverScroll( const SyncCompositorCommonRendererParams& params, const DidOverscrollParams& over_scroll_params) { ProcessCommonParams(params); - client_->DidOverscroll(over_scroll_params.accumulated_overscroll, - over_scroll_params.latest_overscroll_delta, - over_scroll_params.current_fling_velocity); + DidOverscroll(over_scroll_params); } void SynchronousCompositorHost::PopulateCommonParams( @@ -330,6 +414,8 @@ void SynchronousCompositorHost::PopulateCommonParams( DCHECK(params); DCHECK(params->ack.resources.empty()); params->bytes_limit = bytes_limit_; + params->output_surface_id_for_returned_resources = + output_surface_id_from_last_draw_; params->ack.resources.swap(returned_resources_); if (root_scroll_offset_updated_by_browser_) { params->root_scroll_offset = root_scroll_offset_; @@ -354,18 +440,16 @@ void SynchronousCompositorHost::ProcessCommonParams( need_begin_frame_ = params.need_begin_frame; UpdateNeedsBeginFrames(); } - need_invalidate_ = need_invalidate_ || params.need_invalidate; - did_activate_pending_tree_ = - did_activate_pending_tree_ || params.did_activate_pending_tree; root_scroll_offset_ = params.total_scroll_offset; - if (need_invalidate_) { - need_invalidate_ = false; + if (need_invalidate_count_ != params.need_invalidate_count) { + need_invalidate_count_ = params.need_invalidate_count; client_->PostInvalidate(); } - if (did_activate_pending_tree_) { - did_activate_pending_tree_ = false; + if (did_activate_pending_tree_count_ != + params.did_activate_pending_tree_count) { + did_activate_pending_tree_count_ = params.did_activate_pending_tree_count; client_->DidUpdateContent(); } diff --git a/chromium/content/browser/android/synchronous_compositor_host.h b/chromium/content/browser/android/synchronous_compositor_host.h index 8c60071db59..b9b1598602f 100644 --- a/chromium/content/browser/android/synchronous_compositor_host.h +++ b/chromium/content/browser/android/synchronous_compositor_host.h @@ -33,7 +33,7 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { ~SynchronousCompositorHost() override; // SynchronousCompositor overrides. - scoped_ptr DemandDrawHw( + SynchronousCompositor::Frame DemandDrawHw( const gfx::Size& surface_size, const gfx::Transform& transform, const gfx::Rect& viewport, @@ -41,16 +41,19 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { const gfx::Rect& viewport_rect_for_tile_priority, const gfx::Transform& transform_for_tile_priority) override; bool DemandDrawSw(SkCanvas* canvas) override; - void ReturnResources(const cc::CompositorFrameAck& frame_ack) override; + void ReturnResources(uint32_t output_surface_id, + const cc::CompositorFrameAck& frame_ack) override; void SetMemoryPolicy(size_t bytes_limit) override; void DidChangeRootLayerScrollOffset( const gfx::ScrollOffset& root_offset) override; + void SynchronouslyZoomBy(float zoom_delta, const gfx::Point& anchor) override; void SetIsActive(bool is_active) override; void OnComputeScroll(base::TimeTicks animation_time) override; // SynchronousCompositorBase overrides. InputEventAckState HandleInputEvent( const blink::WebInputEvent& input_event) override; + void DidOverscroll(const DidOverscrollParams& over_scroll_params) override; void BeginFrame(const cc::BeginFrameArgs& args) override; bool OnMessageReceived(const IPC::Message& message) override; void DidBecomeCurrent() override; @@ -62,7 +65,9 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { friend class SynchronousCompositorBase; SynchronousCompositorHost(RenderWidgetHostViewAndroid* rwhva, - SynchronousCompositorClient* client); + SynchronousCompositorClient* client, + bool async_input, + bool use_in_proc_software_draw); void PopulateCommonParams(SyncCompositorCommonBrowserParams* params); void ProcessCommonParams(const SyncCompositorCommonRendererParams& params); void UpdateNeedsBeginFrames(); @@ -71,6 +76,7 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { const DidOverscrollParams& over_scroll_params); void SendAsyncCompositorStateIfNeeded(); void UpdateStateTask(); + bool DemandDrawSwInProc(SkCanvas* canvas); void SetSoftwareDrawSharedMemoryIfNeeded(size_t stride, size_t buffer_size); void SendZeroMemory(); @@ -79,9 +85,12 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { const scoped_refptr ui_task_runner_; const int routing_id_; IPC::Sender* const sender_; + const bool async_input_; + const bool use_in_process_zero_copy_software_draw_; bool is_active_; size_t bytes_limit_; + uint32_t output_surface_id_from_last_draw_; cc::ReturnedResourceArray returned_resources_; scoped_ptr software_draw_shm_; @@ -92,9 +101,9 @@ class SynchronousCompositorHost : public SynchronousCompositorBase { // From renderer. uint32_t renderer_param_version_; bool need_animate_scroll_; - bool need_invalidate_; + uint32_t need_invalidate_count_; bool need_begin_frame_; - bool did_activate_pending_tree_; + uint32_t did_activate_pending_tree_count_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorHost); diff --git a/chromium/content/browser/android/web_contents_observer_proxy.cc b/chromium/content/browser/android/web_contents_observer_proxy.cc index d176f61f21b..dfa52cfb211 100644 --- a/chromium/content/browser/android/web_contents_observer_proxy.cc +++ b/chromium/content/browser/android/web_contents_observer_proxy.cc @@ -11,8 +11,10 @@ #include "base/android/scoped_java_ref.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/android/media_metadata_android.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/navigation_handle.h" #include "jni/WebContentsObserverProxy_jni.h" using base::android::AttachCurrentThread; @@ -76,6 +78,17 @@ void WebContentsObserverProxy::RenderProcessGone( was_oom_protected); } +void WebContentsObserverProxy::DidFinishNavigation( + NavigationHandle* navigation_handle) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj(java_observer_); + ScopedJavaLocalRef jstring_url( + ConvertUTF8ToJavaString(env, web_contents()->GetVisibleURL().spec())); + Java_WebContentsObserverProxy_didFinishNavigation( + env, obj.obj(), navigation_handle->IsInMainFrame(), + navigation_handle->IsErrorPage(), navigation_handle->HasCommitted()); +} + void WebContentsObserverProxy::DidStartLoading() { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef obj(java_observer_); @@ -144,6 +157,7 @@ void WebContentsObserverProxy::DidNavigateMainFrame( // that would also be valid for a fragment navigation. bool is_fragment_navigation = urls_same_ignoring_fragment && details.is_in_page; + Java_WebContentsObserverProxy_didNavigateMainFrame( env, obj.obj(), jstring_url.obj(), jstring_base_url.obj(), details.is_navigation_to_different_page(), is_fragment_navigation, @@ -294,14 +308,18 @@ void WebContentsObserverProxy::DidStartNavigationToPendingEntry( env, obj.obj(), jstring_url.obj()); } -void WebContentsObserverProxy::MediaSessionStateChanged(bool is_controllable, - bool is_suspended) { +void WebContentsObserverProxy::MediaSessionStateChanged( + bool is_controllable, + bool is_suspended, + const MediaMetadata& metadata) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef obj(java_observer_); + ScopedJavaLocalRef j_metadata = + MediaMetadataAndroid::CreateJavaObject(env, metadata); Java_WebContentsObserverProxy_mediaSessionStateChanged( - env, obj.obj(), is_controllable, is_suspended); + env, obj.obj(), is_controllable, is_suspended, j_metadata.obj()); } void WebContentsObserverProxy::SetToBaseURLForDataURLIfNeeded( diff --git a/chromium/content/browser/android/web_contents_observer_proxy.h b/chromium/content/browser/android/web_contents_observer_proxy.h index e22edbbdf84..af0895dc669 100644 --- a/chromium/content/browser/android/web_contents_observer_proxy.h +++ b/chromium/content/browser/android/web_contents_observer_proxy.h @@ -32,6 +32,7 @@ class WebContentsObserverProxy : public WebContentsObserver { private: void RenderViewReady() override; void RenderProcessGone(base::TerminationStatus termination_status) override; + void DidFinishNavigation(NavigationHandle* navigation_handle) override; void DidStartLoading() override; void DidStopLoading() override; void DidFailProvisionalLoad(RenderFrameHost* render_frame_host, @@ -72,7 +73,8 @@ class WebContentsObserverProxy : public WebContentsObserver { const GURL& url, NavigationController::ReloadType reload_type) override; void MediaSessionStateChanged(bool is_controllable, - bool is_suspended) override; + bool is_suspended, + const MediaMetadata& metadata) override; void SetToBaseURLForDataURLIfNeeded(std::string* url); void DidFailLoadInternal(bool is_provisional_load, diff --git a/chromium/content/browser/appcache/appcache_database.cc b/chromium/content/browser/appcache/appcache_database.cc index f9e83ac91b7..f3053c7d258 100644 --- a/chromium/content/browser/appcache/appcache_database.cc +++ b/chromium/content/browser/appcache/appcache_database.cc @@ -191,6 +191,8 @@ AppCacheDatabase::GroupRecord::GroupRecord() : group_id(0) { } +AppCacheDatabase::GroupRecord::GroupRecord(const GroupRecord& other) = default; + AppCacheDatabase::GroupRecord::~GroupRecord() { } diff --git a/chromium/content/browser/appcache/appcache_database.h b/chromium/content/browser/appcache/appcache_database.h index a6f017e48d0..e2b265d56f1 100644 --- a/chromium/content/browser/appcache/appcache_database.h +++ b/chromium/content/browser/appcache/appcache_database.h @@ -51,6 +51,7 @@ class CONTENT_EXPORT AppCacheDatabase { public: struct CONTENT_EXPORT GroupRecord { GroupRecord(); + GroupRecord(const GroupRecord& other); ~GroupRecord(); int64_t group_id; diff --git a/chromium/content/browser/appcache/appcache_disk_cache.cc b/chromium/content/browser/appcache/appcache_disk_cache.cc index 49030f000a9..da5475d03d2 100644 --- a/chromium/content/browser/appcache/appcache_disk_cache.cc +++ b/chromium/content/browser/appcache/appcache_disk_cache.cc @@ -336,6 +336,8 @@ AppCacheDiskCache::PendingCall::PendingCall( const net::CompletionCallback& callback) : call_type(call_type), key(key), entry(entry), callback(callback) {} +AppCacheDiskCache::PendingCall::PendingCall(const PendingCall& other) = default; + AppCacheDiskCache::PendingCall::~PendingCall() {} int AppCacheDiskCache::Init( diff --git a/chromium/content/browser/appcache/appcache_disk_cache.h b/chromium/content/browser/appcache/appcache_disk_cache.h index 2b8c29993b4..d06bcf50f5b 100644 --- a/chromium/content/browser/appcache/appcache_disk_cache.h +++ b/chromium/content/browser/appcache/appcache_disk_cache.h @@ -89,6 +89,8 @@ class CONTENT_EXPORT AppCacheDiskCache Entry** entry, const net::CompletionCallback& callback); + PendingCall(const PendingCall& other); + ~PendingCall(); }; typedef std::vector PendingCalls; diff --git a/chromium/content/browser/appcache/appcache_interceptor.cc b/chromium/content/browser/appcache/appcache_interceptor.cc index 81985fe704a..050e66aee18 100644 --- a/chromium/content/browser/appcache/appcache_interceptor.cc +++ b/chromium/content/browser/appcache/appcache_interceptor.cc @@ -9,6 +9,9 @@ #include "content/browser/appcache/appcache_request_handler.h" #include "content/browser/appcache/appcache_service_impl.h" #include "content/browser/appcache/appcache_url_request_job.h" +#include "content/browser/appcache/chrome_appcache_service.h" +#include "content/browser/bad_message.h" +#include "content/browser/loader/resource_message_filter.h" #include "content/common/appcache_interfaces.h" #include "net/url_request/url_request.h" @@ -76,10 +79,16 @@ void AppCacheInterceptor::PrepareForCrossSiteTransfer( void AppCacheInterceptor::CompleteCrossSiteTransfer( net::URLRequest* request, int new_process_id, - int new_host_id) { + int new_host_id, + ResourceMessageFilter* filter) { AppCacheRequestHandler* handler = GetHandler(request); if (!handler) return; + if (!handler->SanityCheckIsSameService(filter->appcache_service())) { + bad_message::ReceivedBadMessage(filter, + bad_message::ACI_WRONG_STORAGE_PARTITION); + return; + } DCHECK_NE(kAppCacheNoHostId, new_host_id); handler->CompleteCrossSiteTransfer(new_process_id, new_host_id); diff --git a/chromium/content/browser/appcache/appcache_interceptor.h b/chromium/content/browser/appcache/appcache_interceptor.h index 0e50983ba21..9d1abc31b6a 100644 --- a/chromium/content/browser/appcache/appcache_interceptor.h +++ b/chromium/content/browser/appcache/appcache_interceptor.h @@ -21,6 +21,7 @@ class URLRequest; namespace content { class AppCacheRequestHandler; class AppCacheServiceImpl; +class ResourceMessageFilter; // An interceptor to hijack requests and potentially service them out of // the appcache. @@ -45,7 +46,8 @@ class CONTENT_EXPORT AppCacheInterceptor : public net::URLRequestInterceptor { int old_process_id); static void CompleteCrossSiteTransfer(net::URLRequest* request, int new_process_id, - int new_host_id); + int new_host_id, + ResourceMessageFilter* filter); static void MaybeCompleteCrossSiteTransferInOldProcess( net::URLRequest* request, int old_process_id); diff --git a/chromium/content/browser/appcache/appcache_internals_ui.cc b/chromium/content/browser/appcache/appcache_internals_ui.cc index 3abd692603a..0c402129b60 100644 --- a/chromium/content/browser/appcache/appcache_internals_ui.cc +++ b/chromium/content/browser/appcache/appcache_internals_ui.cc @@ -462,7 +462,7 @@ void AppCacheInternalsUI::OnFileDetailsReady( response_info->http_response_info()->headers->GetStatusLine())); headers.push_back('\n'); - void* iter = nullptr; + size_t iter = 0; std::string name, value; while (response_info->http_response_info()->headers->EnumerateHeaderLines( &iter, &name, &value)) { diff --git a/chromium/content/browser/appcache/appcache_request_handler.cc b/chromium/content/browser/appcache/appcache_request_handler.cc index c02223752af..0a00878df3c 100644 --- a/chromium/content/browser/appcache/appcache_request_handler.cc +++ b/chromium/content/browser/appcache/appcache_request_handler.cc @@ -187,6 +187,7 @@ void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) { if (!host_) return; AppCacheBackendImpl* backend = host_->service()->GetBackend(old_process_id); + DCHECK(backend) << "appcache detected likely storage partition mismatch"; old_process_id_ = old_process_id; old_host_id_ = host_->host_id(); host_for_cross_site_transfer_ = backend->TransferHostOut(host_->host_id()); @@ -199,6 +200,7 @@ void AppCacheRequestHandler::CompleteCrossSiteTransfer( return; DCHECK_EQ(host_, host_for_cross_site_transfer_.get()); AppCacheBackendImpl* backend = host_->service()->GetBackend(new_process_id); + DCHECK(backend) << "appcache detected likely storage partition mismatch"; backend->TransferHostIn(new_host_id, std::move(host_for_cross_site_transfer_)); } diff --git a/chromium/content/browser/appcache/appcache_request_handler.h b/chromium/content/browser/appcache/appcache_request_handler.h index d7b55e5a96b..7665d10e0b8 100644 --- a/chromium/content/browser/appcache/appcache_request_handler.h +++ b/chromium/content/browser/appcache/appcache_request_handler.h @@ -25,6 +25,7 @@ class URLRequestJob; namespace content { class AppCacheRequestHandlerTest; +class AppCacheService; class AppCacheURLRequestJob; // An instance is created for each net::URLRequest. The instance survives all @@ -58,6 +59,12 @@ class CONTENT_EXPORT AppCacheRequestHandler void CompleteCrossSiteTransfer(int new_process_id, int new_host_id); void MaybeCompleteCrossSiteTransferInOldProcess(int old_process_id); + // Useful for detecting storage partition mismatches in the context + // of cross site transfer navigations. + bool SanityCheckIsSameService(AppCacheService* service) { + return !host_ || (host_->service() == service); + } + static bool IsMainResourceType(ResourceType type) { return IsResourceTypeFrame(type) || type == RESOURCE_TYPE_SHARED_WORKER; diff --git a/chromium/content/browser/appcache/appcache_response.cc b/chromium/content/browser/appcache/appcache_response.cc index 20a8b8a36d0..b5df22dea85 100644 --- a/chromium/content/browser/appcache/appcache_response.cc +++ b/chromium/content/browser/appcache/appcache_response.cc @@ -78,11 +78,24 @@ HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info) HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {} +// AppCacheDiskCacheInterface ---------------------------------------- + +AppCacheDiskCacheInterface::AppCacheDiskCacheInterface() + : weak_factory_(this) {} + +base::WeakPtr +AppCacheDiskCacheInterface::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +AppCacheDiskCacheInterface::~AppCacheDiskCacheInterface() {} + // AppCacheResponseIO ---------------------------------------------- -AppCacheResponseIO::AppCacheResponseIO(int64_t response_id, - int64_t group_id, - AppCacheDiskCacheInterface* disk_cache) +AppCacheResponseIO::AppCacheResponseIO( + int64_t response_id, + int64_t group_id, + const base::WeakPtr& disk_cache) : response_id_(response_id), group_id_(group_id), disk_cache_(disk_cache), @@ -177,7 +190,7 @@ void AppCacheResponseIO::OpenEntryCallback( AppCacheResponseReader::AppCacheResponseReader( int64_t response_id, int64_t group_id, - AppCacheDiskCacheInterface* disk_cache) + const base::WeakPtr& disk_cache) : AppCacheResponseIO(response_id, group_id, disk_cache), range_offset_(0), range_length_(std::numeric_limits::max()), @@ -302,7 +315,7 @@ void AppCacheResponseReader::OnOpenEntryComplete() { AppCacheResponseWriter::AppCacheResponseWriter( int64_t response_id, int64_t group_id, - AppCacheDiskCacheInterface* disk_cache) + const base::WeakPtr& disk_cache) : AppCacheResponseIO(response_id, group_id, disk_cache), info_size_(0), write_position_(0), @@ -404,7 +417,10 @@ void AppCacheResponseWriter::OnCreateEntryComplete( AppCacheDiskCacheInterface::Entry** entry, int rv) { DCHECK(info_buffer_.get() || buffer_.get()); - if (creation_phase_ == INITIAL_ATTEMPT) { + if (!disk_cache_) { + ScheduleIOCompletionCallback(net::ERR_FAILED); + return; + } else if (creation_phase_ == INITIAL_ATTEMPT) { if (rv != net::OK) { // We may try to overwrite existing entries. creation_phase_ = DOOM_EXISTING; @@ -444,7 +460,7 @@ void AppCacheResponseWriter::OnCreateEntryComplete( AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter( int64_t response_id, int64_t group_id, - AppCacheDiskCacheInterface* disk_cache) + const base::WeakPtr& disk_cache) : AppCacheResponseIO(response_id, group_id, disk_cache), write_amount_(0), weak_factory_(this) {} diff --git a/chromium/content/browser/appcache/appcache_response.h b/chromium/content/browser/appcache/appcache_response.h index 8543b2b902f..0b709b91068 100644 --- a/chromium/content/browser/appcache/appcache_response.h +++ b/chromium/content/browser/appcache/appcache_response.h @@ -93,6 +93,8 @@ class CONTENT_EXPORT AppCacheDiskCacheInterface { virtual ~Entry() {} }; + AppCacheDiskCacheInterface(); + virtual int CreateEntry(int64_t key, Entry** entry, const net::CompletionCallback& callback) = 0; @@ -102,9 +104,12 @@ class CONTENT_EXPORT AppCacheDiskCacheInterface { virtual int DoomEntry(int64_t key, const net::CompletionCallback& callback) = 0; + base::WeakPtr GetWeakPtr(); + protected: - friend class base::RefCounted; - virtual ~AppCacheDiskCacheInterface() {} + virtual ~AppCacheDiskCacheInterface(); + + base::WeakPtrFactory weak_factory_; }; // Common base class for response reader and writer. @@ -114,9 +119,10 @@ class CONTENT_EXPORT AppCacheResponseIO { int64_t response_id() const { return response_id_; } protected: - AppCacheResponseIO(int64_t response_id, - int64_t group_id, - AppCacheDiskCacheInterface* disk_cache); + AppCacheResponseIO( + int64_t response_id, + int64_t group_id, + const base::WeakPtr& disk_cache); virtual void OnIOComplete(int result) = 0; virtual void OnOpenEntryComplete() {} @@ -130,7 +136,7 @@ class CONTENT_EXPORT AppCacheResponseIO { const int64_t response_id_; const int64_t group_id_; - AppCacheDiskCacheInterface* disk_cache_; + base::WeakPtr disk_cache_; AppCacheDiskCacheInterface::Entry* entry_; scoped_refptr info_buffer_; scoped_refptr buffer_; @@ -190,9 +196,10 @@ class CONTENT_EXPORT AppCacheResponseReader friend class content::MockAppCacheStorage; // Should only be constructed by the storage class and derivatives. - AppCacheResponseReader(int64_t response_id, - int64_t group_id, - AppCacheDiskCacheInterface* disk_cache); + AppCacheResponseReader( + int64_t response_id, + int64_t group_id, + const base::WeakPtr& disk_cache); void OnIOComplete(int result) override; void OnOpenEntryComplete() override; @@ -247,9 +254,10 @@ class CONTENT_EXPORT AppCacheResponseWriter protected: // Should only be constructed by the storage class and derivatives. - AppCacheResponseWriter(int64_t response_id, - int64_t group_id, - AppCacheDiskCacheInterface* disk_cache); + AppCacheResponseWriter( + int64_t response_id, + int64_t group_id, + const base::WeakPtr& disk_cache); private: friend class AppCacheStorageImpl; @@ -304,10 +312,12 @@ class CONTENT_EXPORT AppCacheResponseMetadataWriter protected: friend class AppCacheStorageImpl; friend class content::MockAppCacheStorage; + // Should only be constructed by the storage class and derivatives. - AppCacheResponseMetadataWriter(int64_t response_id, - int64_t group_id, - AppCacheDiskCacheInterface* disk_cache); + AppCacheResponseMetadataWriter( + int64_t response_id, + int64_t group_id, + const base::WeakPtr& disk_cache); private: void OnIOComplete(int result) override; diff --git a/chromium/content/browser/appcache/appcache_service_impl.cc b/chromium/content/browser/appcache/appcache_service_impl.cc index 2fc5d949d2e..537ca8e81e6 100644 --- a/chromium/content/browser/appcache/appcache_service_impl.cc +++ b/chromium/content/browser/appcache/appcache_service_impl.cc @@ -412,9 +412,8 @@ AppCacheServiceImpl::AppCacheServiceImpl( AppCacheServiceImpl::~AppCacheServiceImpl() { DCHECK(backends_.empty()); - std::for_each(pending_helpers_.begin(), - pending_helpers_.end(), - std::mem_fun(&AsyncHelper::Cancel)); + for (auto* helper : pending_helpers_) + helper->Cancel(); STLDeleteElements(&pending_helpers_); if (quota_client_) quota_client_->NotifyAppCacheDestroyed(); diff --git a/chromium/content/browser/appcache/appcache_service_unittest.cc b/chromium/content/browser/appcache/appcache_service_unittest.cc index 79febf7675a..5c29d50ee5f 100644 --- a/chromium/content/browser/appcache/appcache_service_unittest.cc +++ b/chromium/content/browser/appcache/appcache_service_unittest.cc @@ -42,7 +42,9 @@ class MockResponseReader : public AppCacheResponseReader { int info_size, const char* data, int data_size) - : AppCacheResponseReader(response_id, 0, NULL), + : AppCacheResponseReader(response_id, + 0, + base::WeakPtr()), info_(info), info_size_(info_size), data_(data), diff --git a/chromium/content/browser/appcache/appcache_storage_impl.cc b/chromium/content/browser/appcache/appcache_storage_impl.cc index b144eb69f51..ceb52271fd8 100644 --- a/chromium/content/browser/appcache/appcache_storage_impl.cc +++ b/chromium/content/browser/appcache/appcache_storage_impl.cc @@ -837,11 +837,7 @@ void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() { // Helpers for FindMainResponseTask::Run() namespace { -class SortByCachePreference - : public std::binary_function< - AppCacheDatabase::EntryRecord, - AppCacheDatabase::EntryRecord, - bool> { +class SortByCachePreference { public: SortByCachePreference(int64_t preferred_id, const std::set& in_use_ids) @@ -1423,12 +1419,10 @@ AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl* service) } AppCacheStorageImpl::~AppCacheStorageImpl() { - std::for_each(pending_quota_queries_.begin(), - pending_quota_queries_.end(), - std::mem_fun(&DatabaseTask::CancelCompletion)); - std::for_each(scheduled_database_tasks_.begin(), - scheduled_database_tasks_.end(), - std::mem_fun(&DatabaseTask::CancelCompletion)); + for (auto* task : pending_quota_queries_) + task->CancelCompletion(); + for (auto* task : scheduled_database_tasks_) + task->CancelCompletion(); if (database_ && !db_thread_->PostTask( @@ -1742,20 +1736,22 @@ AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader( const GURL& manifest_url, int64_t group_id, int64_t response_id) { - return new AppCacheResponseReader(response_id, group_id, disk_cache()); + return new AppCacheResponseReader(response_id, group_id, + disk_cache()->GetWeakPtr()); } AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter( const GURL& manifest_url, int64_t group_id) { - return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache()); + return new AppCacheResponseWriter(NewResponseId(), group_id, + disk_cache()->GetWeakPtr()); } AppCacheResponseMetadataWriter* AppCacheStorageImpl::CreateResponseMetadataWriter(int64_t group_id, int64_t response_id) { return new AppCacheResponseMetadataWriter(response_id, group_id, - disk_cache()); + disk_cache()->GetWeakPtr()); } void AppCacheStorageImpl::DoomResponses( diff --git a/chromium/content/browser/appcache/appcache_update_job.cc b/chromium/content/browser/appcache/appcache_update_job.cc index b6ab3fb7318..7c922f868a7 100644 --- a/chromium/content/browser/appcache/appcache_update_job.cc +++ b/chromium/content/browser/appcache/appcache_update_job.cc @@ -140,6 +140,8 @@ AppCacheUpdateJob::UrlToFetch::UrlToFetch(const GURL& url, existing_response_info(info) { } +AppCacheUpdateJob::UrlToFetch::UrlToFetch(const UrlToFetch& other) = default; + AppCacheUpdateJob::UrlToFetch::~UrlToFetch() { } @@ -279,7 +281,7 @@ void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders( // Add If-Modified-Since header if response info has Last-Modified header. const std::string last_modified = "Last-Modified"; std::string last_modified_value; - headers->EnumerateHeader(NULL, last_modified, &last_modified_value); + headers->EnumerateHeader(nullptr, last_modified, &last_modified_value); if (!last_modified_value.empty()) { extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince, last_modified_value); @@ -288,7 +290,7 @@ void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders( // Add If-None-Match header if response info has ETag header. const std::string etag = "ETag"; std::string etag_value; - headers->EnumerateHeader(NULL, etag, &etag_value); + headers->EnumerateHeader(nullptr, etag, &etag_value); if (!etag_value.empty()) { extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, etag_value); @@ -1474,7 +1476,7 @@ void AppCacheUpdateJob::OnResponseInfoLoaded( // Responses with a "vary" header get treated as expired. const std::string name = "vary"; std::string value; - void* iter = NULL; + size_t iter = 0; if (!http_info->headers.get() || http_info->headers->RequiresValidation(http_info->request_time, http_info->response_time, diff --git a/chromium/content/browser/appcache/appcache_update_job.h b/chromium/content/browser/appcache/appcache_update_job.h index f1c7af03bd5..4fa15885c88 100644 --- a/chromium/content/browser/appcache/appcache_update_job.h +++ b/chromium/content/browser/appcache/appcache_update_job.h @@ -101,6 +101,7 @@ class CONTENT_EXPORT AppCacheUpdateJob struct UrlToFetch { UrlToFetch(const GURL& url, bool checked, AppCacheResponseInfo* info); + UrlToFetch(const UrlToFetch& other); ~UrlToFetch(); GURL url; diff --git a/chromium/content/browser/appcache/appcache_url_request_job.cc b/chromium/content/browser/appcache/appcache_url_request_job.cc index 05fe008b54e..35728e66de5 100644 --- a/chromium/content/browser/appcache/appcache_url_request_job.cc +++ b/chromium/content/browser/appcache/appcache_url_request_job.cc @@ -433,6 +433,12 @@ int AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size) { return net::ERR_IO_PENDING; } +net::HostPortPair AppCacheURLRequestJob::GetSocketAddress() const { + if (!http_info()) + return net::HostPortPair(); + return http_info()->socket_address; +} + void AppCacheURLRequestJob::SetExtraRequestHeaders( const net::HttpRequestHeaders& headers) { std::string value; diff --git a/chromium/content/browser/appcache/appcache_url_request_job.h b/chromium/content/browser/appcache/appcache_url_request_job.h index 057c13b98e4..5b8723761a5 100644 --- a/chromium/content/browser/appcache/appcache_url_request_job.h +++ b/chromium/content/browser/appcache/appcache_url_request_job.h @@ -152,6 +152,7 @@ class CONTENT_EXPORT AppCacheURLRequestJob bool GetCharset(std::string* charset) override; void GetResponseInfo(net::HttpResponseInfo* info) override; int ReadRawData(net::IOBuffer* buf, int buf_size) override; + net::HostPortPair GetSocketAddress() const override; // Sets extra request headers for Job types that support request headers. // This is how we get informed of range-requests. diff --git a/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc b/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc index 0eca5fe0cfb..a23bde6f0e5 100644 --- a/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc +++ b/chromium/content/browser/appcache/chrome_appcache_service_unittest.cc @@ -102,14 +102,11 @@ ChromeAppCacheServiceTest::CreateAppCacheServiceImpl( browser_context_.GetResourceContext()->GetRequestContext(), message_loop_.task_runner().get()); BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, + BrowserThread::IO, FROM_HERE, base::Bind(&ChromeAppCacheService::InitializeOnIOThread, - appcache_service.get(), - appcache_path, + appcache_service.get(), appcache_path, browser_context_.GetResourceContext(), - mock_request_context_getter, - mock_policy)); + base::RetainedRef(mock_request_context_getter), mock_policy)); // Steps needed to initialize the storage of AppCache data. message_loop_.RunUntilIdle(); if (init_storage) { diff --git a/chromium/content/browser/appcache/mock_appcache_storage.cc b/chromium/content/browser/appcache/mock_appcache_storage.cc index 09bdf03c2ac..5d7cd8d19d9 100644 --- a/chromium/content/browser/appcache/mock_appcache_storage.cc +++ b/chromium/content/browser/appcache/mock_appcache_storage.cc @@ -169,20 +169,22 @@ AppCacheResponseReader* MockAppCacheStorage::CreateResponseReader( int64_t response_id) { if (simulated_reader_) return simulated_reader_.release(); - return new AppCacheResponseReader(response_id, group_id, disk_cache()); + return new AppCacheResponseReader(response_id, group_id, + disk_cache()->GetWeakPtr()); } AppCacheResponseWriter* MockAppCacheStorage::CreateResponseWriter( const GURL& manifest_url, int64_t group_id) { - return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache()); + return new AppCacheResponseWriter(NewResponseId(), group_id, + disk_cache()->GetWeakPtr()); } AppCacheResponseMetadataWriter* MockAppCacheStorage::CreateResponseMetadataWriter(int64_t group_id, int64_t response_id) { return new AppCacheResponseMetadataWriter(response_id, group_id, - disk_cache()); + disk_cache()->GetWeakPtr()); } void MockAppCacheStorage::DoomResponses( diff --git a/chromium/content/browser/background_sync/background_sync.proto b/chromium/content/browser/background_sync/background_sync.proto index 11adc3133c3..62dbe72b359 100644 --- a/chromium/content/browser/background_sync/background_sync.proto +++ b/chromium/content/browser/background_sync/background_sync.proto @@ -14,23 +14,13 @@ enum SyncNetworkState { NETWORK_STATE_ONLINE = 2; } -enum SyncPowerState { - POWER_STATE_AUTO = 0; - POWER_STATE_AVOID_DRAINING = 1; -} - -enum SyncPeriodicity { - SYNC_PERIODIC = 0; - SYNC_ONE_SHOT = 1; -} - message BackgroundSyncRegistrationProto { required int64 id = 1; required string tag = 2; - required SyncPeriodicity periodicity = 3; - required int64 min_period = 4; + // required SyncPeriodicity periodicity = 3; + // required int64 min_period = 4; required SyncNetworkState network_state = 5; - required SyncPowerState power_state = 6; + // required SyncPowerState power_state = 6; required int32 num_attempts = 7; required int64 delay_until = 8; } diff --git a/chromium/content/browser/background_sync/background_sync_browsertest.cc b/chromium/content/browser/background_sync/background_sync_browsertest.cc index a5de6ada408..4113137ef62 100644 --- a/chromium/content/browser/background_sync/background_sync_browsertest.cc +++ b/chromium/content/browser/background_sync/background_sync_browsertest.cc @@ -15,7 +15,6 @@ #include "base/task_runner_util.h" #include "content/browser/background_sync/background_sync_manager.h" #include "content/browser/background_sync/background_sync_network_observer.h" -#include "content/browser/background_sync/background_sync_registration_handle.h" #include "content/browser/background_sync/background_sync_status.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_registration.h" @@ -42,7 +41,10 @@ namespace { const char kDefaultTestURL[] = "/background_sync/test.html"; const char kEmptyURL[] = "/background_sync/empty.html"; -const char kRegisterSyncURL[] = "/background_sync/register_sync.html"; +const char kRegisterSyncFromIFrameURL[] = + "/background_sync/register_sync_from_iframe.html"; +const char kRegisterSyncFromSWURL[] = + "/background_sync/register_sync_from_sw.html"; const char kSuccessfulOperationPrefix[] = "ok - "; @@ -57,7 +59,7 @@ std::string BuildExpectedResult(const std::string& tag, action.c_str()); } -void OneShotPendingCallback( +void RegistrationPendingCallback( const base::Closure& quit, const scoped_refptr& task_runner, bool* result_out, @@ -66,16 +68,24 @@ void OneShotPendingCallback( task_runner->PostTask(FROM_HERE, quit); } -void OneShotPendingDidGetSyncRegistration( +void RegistrationPendingDidGetSyncRegistration( + const std::string& tag, const base::Callback& callback, BackgroundSyncStatus error_type, - scoped_ptr registration_handle) { + scoped_ptr> registrations) { ASSERT_EQ(BACKGROUND_SYNC_STATUS_OK, error_type); - callback.Run(registration_handle->sync_state() == - BACKGROUND_SYNC_STATE_PENDING); + // Find the right registration in the list and check its status. + for (const BackgroundSyncRegistration* registration : *registrations) { + if (registration->options()->tag == tag) { + callback.Run(registration->sync_state() == + mojom::BackgroundSyncState::PENDING); + return; + } + } + ADD_FAILURE() << "Registration should exist"; } -void OneShotPendingDidGetSWRegistration( +void RegistrationPendingDidGetSWRegistration( const scoped_refptr sync_context, const std::string& tag, const base::Callback& callback, @@ -84,20 +94,20 @@ void OneShotPendingDidGetSWRegistration( ASSERT_EQ(SERVICE_WORKER_OK, status); int64_t service_worker_id = registration->id(); BackgroundSyncManager* sync_manager = sync_context->background_sync_manager(); - sync_manager->GetRegistration( - service_worker_id, tag, SYNC_ONE_SHOT, - base::Bind(&OneShotPendingDidGetSyncRegistration, callback)); + sync_manager->GetRegistrations( + service_worker_id, + base::Bind(&RegistrationPendingDidGetSyncRegistration, tag, callback)); } -void OneShotPendingOnIOThread( +void RegistrationPendingOnIOThread( const scoped_refptr sync_context, const scoped_refptr sw_context, const std::string& tag, const GURL& url, const base::Callback& callback) { sw_context->FindReadyRegistrationForDocument( - url, base::Bind(&OneShotPendingDidGetSWRegistration, sync_context, tag, - callback)); + url, base::Bind(&RegistrationPendingDidGetSWRegistration, sync_context, + tag, callback)); } void SetMaxSyncAttemptsOnIOThread( @@ -169,9 +179,9 @@ class BackgroundSyncBrowserTest : public ContentBrowserTest { result); } - // Returns true if the one-shot sync with tag is currently pending. Fails + // Returns true if the registration with tag |tag| is currently pending. Fails // (assertion failure) if the tag isn't registered. - bool OneShotPending(const std::string& tag); + bool RegistrationPending(const std::string& tag); // Sets the BackgroundSyncManager's max sync attempts per registration. void SetMaxSyncAttempts(int max_sync_attempts); @@ -181,17 +191,18 @@ class BackgroundSyncBrowserTest : public ContentBrowserTest { std::string PopConsoleString(); bool PopConsole(const std::string& expected_msg); bool RegisterServiceWorker(); - bool RegisterOneShot(const std::string& tag); - bool RegisterOneShotFromServiceWorker(const std::string& tag); - bool GetRegistrationOneShot(const std::string& tag); - bool GetRegistrationOneShotFromServiceWorker(const std::string& tag); - bool MatchRegistrations(const std::string& script_result, - const std::vector& expected_tags); - bool GetRegistrationsOneShot(const std::vector& expected_tags); - bool GetRegistrationsOneShotFromServiceWorker( - const std::vector& expected_tags); - bool CompleteDelayedOneShot(); - bool RejectDelayedOneShot(); + bool Register(const std::string& tag); + bool RegisterFromServiceWorker(const std::string& tag); + bool RegisterFromCrossOriginFrame(const std::string& frame_url, + std::string* script_result); + bool HasTag(const std::string& tag); + bool HasTagFromServiceWorker(const std::string& tag); + bool MatchTags(const std::string& script_result, + const std::vector& expected_tags); + bool GetTags(const std::vector& expected_tags); + bool GetTagsFromServiceWorker(const std::vector& expected_tags); + bool CompleteDelayedSyncEvent(); + bool RejectDelayedSyncEvent(); net::EmbeddedTestServer* https_server() { return https_server_.get(); } @@ -202,7 +213,7 @@ class BackgroundSyncBrowserTest : public ContentBrowserTest { DISALLOW_COPY_AND_ASSIGN(BackgroundSyncBrowserTest); }; -bool BackgroundSyncBrowserTest::OneShotPending(const std::string& tag) { +bool BackgroundSyncBrowserTest::RegistrationPending(const std::string& tag) { bool is_pending; base::RunLoop run_loop; @@ -213,12 +224,13 @@ bool BackgroundSyncBrowserTest::OneShotPending(const std::string& tag) { storage->GetServiceWorkerContext()); base::Callback callback = - base::Bind(&OneShotPendingCallback, run_loop.QuitClosure(), + base::Bind(&RegistrationPendingCallback, run_loop.QuitClosure(), base::ThreadTaskRunnerHandle::Get(), &is_pending); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&OneShotPendingOnIOThread, make_scoped_refptr(sync_context), + base::Bind(&RegistrationPendingOnIOThread, + make_scoped_refptr(sync_context), make_scoped_refptr(service_worker_context), tag, https_server_->GetURL(kDefaultTestURL), callback)); @@ -281,41 +293,51 @@ bool BackgroundSyncBrowserTest::RegisterServiceWorker() { return script_result == BuildExpectedResult("service worker", "registered"); } -bool BackgroundSyncBrowserTest::RegisterOneShot(const std::string& tag) { +bool BackgroundSyncBrowserTest::Register(const std::string& tag) { std::string script_result; - EXPECT_TRUE( - RunScript(BuildScriptString("registerOneShot", tag), &script_result)); + EXPECT_TRUE(RunScript(BuildScriptString("register", tag), &script_result)); return script_result == BuildExpectedResult(tag, "registered"); } -bool BackgroundSyncBrowserTest::RegisterOneShotFromServiceWorker( +bool BackgroundSyncBrowserTest::RegisterFromServiceWorker( const std::string& tag) { std::string script_result; - EXPECT_TRUE( - RunScript(BuildScriptString("registerOneShotFromServiceWorker", tag), - &script_result)); + EXPECT_TRUE(RunScript(BuildScriptString("registerFromServiceWorker", tag), + &script_result)); return script_result == BuildExpectedResult(tag, "register sent to SW"); } -bool BackgroundSyncBrowserTest::GetRegistrationOneShot(const std::string& tag) { +bool BackgroundSyncBrowserTest::RegisterFromCrossOriginFrame( + const std::string& frame_url, + std::string* script_result) { + // Start a second https server to use as a second origin. + net::EmbeddedTestServer alt_server(net::EmbeddedTestServer::TYPE_HTTPS); + alt_server.ServeFilesFromSourceDirectory("content/test/data"); + EXPECT_TRUE(alt_server.Start()); + + GURL url = alt_server.GetURL(frame_url); + return RunScript( + BuildScriptString("registerFromCrossOriginFrame", url.spec()), + script_result); +} + +bool BackgroundSyncBrowserTest::HasTag(const std::string& tag) { std::string script_result; - EXPECT_TRUE(RunScript(BuildScriptString("getRegistrationOneShot", tag), - &script_result)); + EXPECT_TRUE(RunScript(BuildScriptString("hasTag", tag), &script_result)); return script_result == BuildExpectedResult(tag, "found"); } -bool BackgroundSyncBrowserTest::GetRegistrationOneShotFromServiceWorker( +bool BackgroundSyncBrowserTest::HasTagFromServiceWorker( const std::string& tag) { std::string script_result; - EXPECT_TRUE(RunScript( - BuildScriptString("getRegistrationOneShotFromServiceWorker", tag), - &script_result)); - EXPECT_TRUE(script_result == "ok - getRegistration sent to SW"); + EXPECT_TRUE(RunScript(BuildScriptString("hasTagFromServiceWorker", tag), + &script_result)); + EXPECT_TRUE(script_result == "ok - hasTag sent to SW"); return PopConsole(BuildExpectedResult(tag, "found")); } -bool BackgroundSyncBrowserTest::MatchRegistrations( +bool BackgroundSyncBrowserTest::MatchTags( const std::string& script_result, const std::vector& expected_tags) { EXPECT_TRUE(base::StartsWith(script_result, kSuccessfulOperationPrefix, @@ -329,79 +351,80 @@ bool BackgroundSyncBrowserTest::MatchRegistrations( std::set(result_tags.begin(), result_tags.end()); } -bool BackgroundSyncBrowserTest::GetRegistrationsOneShot( +bool BackgroundSyncBrowserTest::GetTags( const std::vector& expected_tags) { std::string script_result; - EXPECT_TRUE(RunScript("getRegistrationsOneShot()", &script_result)); + EXPECT_TRUE(RunScript("getTags()", &script_result)); - return MatchRegistrations(script_result, expected_tags); + return MatchTags(script_result, expected_tags); } -bool BackgroundSyncBrowserTest::GetRegistrationsOneShotFromServiceWorker( +bool BackgroundSyncBrowserTest::GetTagsFromServiceWorker( const std::vector& expected_tags) { std::string script_result; - EXPECT_TRUE( - RunScript("getRegistrationsOneShotFromServiceWorker()", &script_result)); - EXPECT_TRUE(script_result == "ok - getRegistrations sent to SW"); + EXPECT_TRUE(RunScript("getTagsFromServiceWorker()", &script_result)); + EXPECT_TRUE(script_result == "ok - getTags sent to SW"); - return MatchRegistrations(PopConsoleString(), expected_tags); + return MatchTags(PopConsoleString(), expected_tags); } -bool BackgroundSyncBrowserTest::CompleteDelayedOneShot() { +bool BackgroundSyncBrowserTest::CompleteDelayedSyncEvent() { std::string script_result; - EXPECT_TRUE(RunScript("completeDelayedOneShot()", &script_result)); + EXPECT_TRUE(RunScript("completeDelayedSyncEvent()", &script_result)); return script_result == BuildExpectedResult("delay", "completing"); } -bool BackgroundSyncBrowserTest::RejectDelayedOneShot() { +bool BackgroundSyncBrowserTest::RejectDelayedSyncEvent() { std::string script_result; - EXPECT_TRUE(RunScript("rejectDelayedOneShot()", &script_result)); + EXPECT_TRUE(RunScript("rejectDelayedSyncEvent()", &script_result)); return script_result == BuildExpectedResult("delay", "rejecting"); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotFiresControlled) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + RegisterFromControlledDocument) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. - EXPECT_TRUE(RegisterOneShot("foo")); + EXPECT_TRUE(Register("foo")); EXPECT_TRUE(PopConsole("foo fired")); - EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_FALSE(HasTag("foo")); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotFiresUncontrolled) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + RegisterFromUncontrolledDocument) { EXPECT_TRUE(RegisterServiceWorker()); - EXPECT_TRUE(RegisterOneShot("foo")); + EXPECT_TRUE(Register("foo")); EXPECT_TRUE(PopConsole("foo fired")); - EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_FALSE(HasTag("foo")); } // Verify that Register works in a service worker -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, - OneShotFromServiceWorkerFires) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromServiceWorker) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. - EXPECT_TRUE(RegisterOneShotFromServiceWorker("foo_sw")); + EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); EXPECT_TRUE(PopConsole("foo_sw fired")); - EXPECT_FALSE(GetRegistrationOneShot("foo_sw")); + EXPECT_FALSE(HasTag("foo_sw")); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, OneShotDelaysForNetwork) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + RegistrationDelaysForNetwork) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. // Prevent firing by going offline. background_sync_test_util::SetOnline(web_contents(), false); - EXPECT_TRUE(RegisterOneShot("foo")); - EXPECT_TRUE(GetRegistrationOneShot("foo")); - EXPECT_TRUE(OneShotPending("foo")); + EXPECT_TRUE(Register("foo")); + EXPECT_TRUE(HasTag("foo")); + EXPECT_TRUE(RegistrationPending("foo")); // Resume firing by going online. background_sync_test_util::SetOnline(web_contents(), true); EXPECT_TRUE(PopConsole("foo fired")); - EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_FALSE(HasTag("foo")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntil) { @@ -409,18 +432,18 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntil) { EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); - EXPECT_TRUE(RegisterOneShot("delay")); + EXPECT_TRUE(Register("delay")); // Verify that it is firing. - EXPECT_TRUE(GetRegistrationOneShot("delay")); - EXPECT_FALSE(OneShotPending("delay")); + EXPECT_TRUE(HasTag("delay")); + EXPECT_FALSE(RegistrationPending("delay")); // Complete the task. - EXPECT_TRUE(CompleteDelayedOneShot()); + EXPECT_TRUE(CompleteDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay completed")); // Verify that it finished firing. - EXPECT_FALSE(GetRegistrationOneShot("delay")); + EXPECT_FALSE(HasTag("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntilReject) { @@ -428,16 +451,16 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, WaitUntilReject) { EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); - EXPECT_TRUE(RegisterOneShot("delay")); + EXPECT_TRUE(Register("delay")); // Verify that it is firing. - EXPECT_TRUE(GetRegistrationOneShot("delay")); - EXPECT_FALSE(OneShotPending("delay")); + EXPECT_TRUE(HasTag("delay")); + EXPECT_FALSE(RegistrationPending("delay")); // Complete the task. - EXPECT_TRUE(RejectDelayedOneShot()); + EXPECT_TRUE(RejectDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay rejected")); - EXPECT_FALSE(GetRegistrationOneShot("delay")); + EXPECT_FALSE(HasTag("delay")); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, Incognito) { @@ -445,8 +468,8 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, Incognito) { EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), false); - EXPECT_TRUE(RegisterOneShot("normal")); - EXPECT_TRUE(OneShotPending("normal")); + EXPECT_TRUE(Register("normal")); + EXPECT_TRUE(RegistrationPending("normal")); // Go incognito and verify that incognito doesn't see the registration. SetIncognitoMode(true); @@ -459,34 +482,34 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, Incognito) { EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); EXPECT_TRUE(RegisterServiceWorker()); - EXPECT_FALSE(GetRegistrationOneShot("normal")); + EXPECT_FALSE(HasTag("normal")); - EXPECT_TRUE(RegisterOneShot("incognito")); - EXPECT_TRUE(OneShotPending("incognito")); + EXPECT_TRUE(Register("incognito")); + EXPECT_TRUE(RegistrationPending("incognito")); // Switch back and make sure the registration is still there. SetIncognitoMode(false); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Should be controlled. - EXPECT_TRUE(GetRegistrationOneShot("normal")); - EXPECT_FALSE(GetRegistrationOneShot("incognito")); + EXPECT_TRUE(HasTag("normal")); + EXPECT_FALSE(HasTag("incognito")); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, GetRegistrations) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, GetTags) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo"); registered_tags.push_back("bar"); for (const std::string& tag : registered_tags) - EXPECT_TRUE(RegisterOneShot(tag)); + EXPECT_TRUE(Register(tag)); - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); } // Verify that GetRegistrations works in a service worker @@ -496,34 +519,33 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo_sw"); registered_tags.push_back("bar_sw"); for (const std::string& tag : registered_tags) { - EXPECT_TRUE(RegisterOneShotFromServiceWorker(tag)); + EXPECT_TRUE(RegisterFromServiceWorker(tag)); EXPECT_TRUE(PopConsole(BuildExpectedResult(tag, "registered in SW"))); } - EXPECT_TRUE(GetRegistrationsOneShotFromServiceWorker(registered_tags)); + EXPECT_TRUE(GetTagsFromServiceWorker(registered_tags)); } // Verify that GetRegistration works in a service worker -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, - GetRegistrationFromServiceWorker) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, HasTagFromServiceWorker) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); - EXPECT_TRUE(RegisterOneShotFromServiceWorker("foo_sw")); + EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); - EXPECT_TRUE(GetRegistrationOneShotFromServiceWorker("foo_sw")); + EXPECT_TRUE(HasTagFromServiceWorker("foo_sw")); } // Verify that a background sync registration is deleted when site data is @@ -535,15 +557,15 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, // Prevent firing by going offline. background_sync_test_util::SetOnline(web_contents(), false); - EXPECT_TRUE(RegisterOneShot("foo")); - EXPECT_TRUE(GetRegistrationOneShot("foo")); - EXPECT_TRUE(OneShotPending("foo")); + EXPECT_TRUE(Register("foo")); + EXPECT_TRUE(HasTag("foo")); + EXPECT_TRUE(RegistrationPending("foo")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); - EXPECT_FALSE(GetRegistrationOneShot("foo")); + EXPECT_FALSE(HasTag("foo")); } // Verify that a background sync registration, from a service worker, is deleted @@ -554,19 +576,19 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); - EXPECT_TRUE(RegisterOneShotFromServiceWorker("foo_sw")); + EXPECT_TRUE(RegisterFromServiceWorker("foo_sw")); EXPECT_TRUE(PopConsole("ok - foo_sw registered in SW")); - EXPECT_TRUE(GetRegistrationOneShotFromServiceWorker("foo_sw")); + EXPECT_TRUE(HasTagFromServiceWorker("foo_sw")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); - EXPECT_FALSE(GetRegistrationOneShotFromServiceWorker("foo")); + EXPECT_FALSE(HasTagFromServiceWorker("foo")); } // Verify that multiple background sync registrations are deleted when site @@ -577,26 +599,26 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. std::vector registered_tags; - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); background_sync_test_util::SetOnline(web_contents(), false); registered_tags.push_back("foo"); registered_tags.push_back("bar"); for (const std::string& tag : registered_tags) - EXPECT_TRUE(RegisterOneShot(tag)); + EXPECT_TRUE(Register(tag)); - EXPECT_TRUE(GetRegistrationsOneShot(registered_tags)); + EXPECT_TRUE(GetTags(registered_tags)); for (const std::string& tag : registered_tags) - EXPECT_TRUE(OneShotPending(tag)); + EXPECT_TRUE(RegistrationPending(tag)); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); for (const std::string& tag : registered_tags) - EXPECT_FALSE(GetRegistrationOneShot(tag)); + EXPECT_FALSE(HasTag(tag)); } // Verify that a sync event that is currently firing is deleted when site @@ -607,58 +629,59 @@ IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. background_sync_test_util::SetOnline(web_contents(), true); - EXPECT_TRUE(RegisterOneShot("delay")); + EXPECT_TRUE(Register("delay")); // Verify that it is firing. - EXPECT_TRUE(GetRegistrationOneShot("delay")); - EXPECT_FALSE(OneShotPending("delay")); + EXPECT_TRUE(HasTag("delay")); + EXPECT_FALSE(RegistrationPending("delay")); // Simulate a user clearing site data (including Service Workers, crucially), // by clearing data from the storage partition. ClearStoragePartitionData(); // Verify that it was deleted. - EXPECT_FALSE(GetRegistrationOneShot("delay")); + EXPECT_FALSE(HasTag("delay")); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, VerifyRetry) { +// Disabled due to flakiness. See https://crbug.com/578952. +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, DISABLED_VerifyRetry) { EXPECT_TRUE(RegisterServiceWorker()); EXPECT_TRUE(LoadTestPage(kDefaultTestURL)); // Control the page. SetMaxSyncAttempts(2); - EXPECT_TRUE(RegisterOneShot("delay")); - EXPECT_TRUE(RejectDelayedOneShot()); + EXPECT_TRUE(Register("delay")); + EXPECT_TRUE(RejectDelayedSyncEvent()); EXPECT_TRUE(PopConsole("ok - delay rejected")); - // Verify that the oneshot is still around and waiting to try again. - EXPECT_TRUE(OneShotPending("delay")); + // Verify that the registration is still around and waiting to try again. + EXPECT_TRUE(RegistrationPending("delay")); } -IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromNonMainFrame) { +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + RegisterFromIFrameWithMainFrameHost) { std::string script_result; GURL url = https_server()->GetURL(kEmptyURL); + EXPECT_TRUE(RunScript(BuildScriptString("registerFromLocalFrame", url.spec()), + &script_result)); + EXPECT_EQ(BuildExpectedResult("iframe", "registered sync"), script_result); +} + +IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, + RegisterFromIFrameWithoutMainFrameHost) { + std::string script_result; EXPECT_TRUE( - RunScript(BuildScriptString("registerOneShotFromLocalFrame", url.spec()), - &script_result)); - EXPECT_EQ(BuildExpectedResult("iframe", "failed to register sync"), + RegisterFromCrossOriginFrame(kRegisterSyncFromIFrameURL, &script_result)); + EXPECT_EQ(BuildExpectedResult("frame", "failed to register sync"), script_result); } IN_PROC_BROWSER_TEST_F(BackgroundSyncBrowserTest, RegisterFromServiceWorkerWithoutMainFrameHost) { - // Start a second https server to use as a second origin. - net::EmbeddedTestServer alt_server(net::EmbeddedTestServer::TYPE_HTTPS); - alt_server.ServeFilesFromSourceDirectory("content/test/data"); - ASSERT_TRUE(alt_server.Start()); - std::string script_result; - GURL url = alt_server.GetURL(kRegisterSyncURL); EXPECT_TRUE( - RunScript(BuildScriptString("registerOneShotFromCrossOriginServiceWorker", - url.spec()), - &script_result)); - EXPECT_EQ(BuildExpectedResult("worker", "failed to register sync"), + RegisterFromCrossOriginFrame(kRegisterSyncFromSWURL, &script_result)); + EXPECT_EQ(BuildExpectedResult("frame", "failed to register sync"), script_result); } diff --git a/chromium/content/browser/background_sync/background_sync_context_impl.cc b/chromium/content/browser/background_sync/background_sync_context_impl.cc index 009e8864b6f..bd92edb8650 100644 --- a/chromium/content/browser/background_sync/background_sync_context_impl.cc +++ b/chromium/content/browser/background_sync/background_sync_context_impl.cc @@ -43,7 +43,7 @@ void BackgroundSyncContextImpl::Shutdown() { } void BackgroundSyncContextImpl::CreateService( - mojo::InterfaceRequest request) { + mojo::InterfaceRequest request) { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( @@ -68,8 +68,15 @@ BackgroundSyncManager* BackgroundSyncContextImpl::background_sync_manager() return background_sync_manager_.get(); } +void BackgroundSyncContextImpl::set_background_sync_manager_for_testing( + scoped_ptr manager) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + background_sync_manager_ = std::move(manager); +} + void BackgroundSyncContextImpl::CreateBackgroundSyncManager( - const scoped_refptr& context) { + scoped_refptr context) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!background_sync_manager_); @@ -77,7 +84,7 @@ void BackgroundSyncContextImpl::CreateBackgroundSyncManager( } void BackgroundSyncContextImpl::CreateServiceOnIOThread( - mojo::InterfaceRequest request) { + mojo::InterfaceRequest request) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(background_sync_manager_); services_.insert(new BackgroundSyncServiceImpl(this, std::move(request))); diff --git a/chromium/content/browser/background_sync/background_sync_context_impl.h b/chromium/content/browser/background_sync/background_sync_context_impl.h index f30a6392728..1eebe700256 100644 --- a/chromium/content/browser/background_sync/background_sync_context_impl.h +++ b/chromium/content/browser/background_sync/background_sync_context_impl.h @@ -37,7 +37,8 @@ class CONTENT_EXPORT BackgroundSyncContextImpl : public BackgroundSyncContext { // Create a BackgroundSyncServiceImpl that is owned by this. Call on the UI // thread. - void CreateService(mojo::InterfaceRequest request); + void CreateService( + mojo::InterfaceRequest request); // Called by BackgroundSyncServiceImpl objects so that they can // be deleted. Call on the IO thread. @@ -49,14 +50,17 @@ class CONTENT_EXPORT BackgroundSyncContextImpl : public BackgroundSyncContext { protected: ~BackgroundSyncContextImpl() override; + void set_background_sync_manager_for_testing( + scoped_ptr manager); + private: friend class BackgroundSyncServiceImplTest; - void CreateBackgroundSyncManager( - const scoped_refptr& context); + virtual void CreateBackgroundSyncManager( + scoped_refptr context); void CreateServiceOnIOThread( - mojo::InterfaceRequest request); + mojo::InterfaceRequest request); void ShutdownOnIO(); diff --git a/chromium/content/browser/background_sync/background_sync_manager.cc b/chromium/content/browser/background_sync/background_sync_manager.cc index 926dbca6dc8..343dd05a4a4 100644 --- a/chromium/content/browser/background_sync/background_sync_manager.cc +++ b/chromium/content/browser/background_sync/background_sync_manager.cc @@ -15,8 +15,6 @@ #include "build/build_config.h" #include "content/browser/background_sync/background_sync_metrics.h" #include "content/browser/background_sync/background_sync_network_observer.h" -#include "content/browser/background_sync/background_sync_power_observer.h" -#include "content/browser/background_sync/background_sync_registration_handle.h" #include "content/browser/background_sync/background_sync_registration_options.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_storage.h" @@ -25,6 +23,8 @@ #include "content/public/browser/background_sync_controller.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/permission_manager.h" +#include "content/public/browser/permission_type.h" #include "content/public/common/background_sync.mojom.h" #if defined(OS_ANDROID) @@ -33,35 +33,24 @@ namespace content { -class BackgroundSyncManager::RefCountedRegistration - : public base::RefCounted { - public: - BackgroundSyncRegistration* value() { return ®istration_; } - const BackgroundSyncRegistration* value() const { return ®istration_; } - - private: - friend class base::RefCounted; - ~RefCountedRegistration() = default; - - BackgroundSyncRegistration registration_; -}; - namespace { // The key used to index the background sync data in ServiceWorkerStorage. const char kBackgroundSyncUserDataKey[] = "BackgroundSyncUserData"; -void PostErrorResponse( +void RecordFailureAndPostError( BackgroundSyncStatus status, const BackgroundSyncManager::StatusAndRegistrationCallback& callback) { + BackgroundSyncMetrics::CountRegisterFailure(status); + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, status, - base::Passed(scoped_ptr()))); + base::Passed(scoped_ptr()))); } -// Returns nullptr if the controller cannot be accessed for any reason. -BackgroundSyncController* GetBackgroundSyncControllerOnUIThread( +// Returns nullptr if the browser context cannot be accessed for any reason. +BrowserContext* GetBrowserContextOnUIThread( const scoped_refptr& service_worker_context) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -72,8 +61,42 @@ BackgroundSyncController* GetBackgroundSyncControllerOnUIThread( if (!storage_partition_impl) // may be null in tests return nullptr; - return storage_partition_impl->browser_context() - ->GetBackgroundSyncController(); + return storage_partition_impl->browser_context(); +} + +// Returns nullptr if the controller cannot be accessed for any reason. +BackgroundSyncController* GetBackgroundSyncControllerOnUIThread( + const scoped_refptr& service_worker_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BrowserContext* browser_context = + GetBrowserContextOnUIThread(service_worker_context); + if (!browser_context) + return nullptr; + + return browser_context->GetBackgroundSyncController(); +} + +// Returns PermissionStatus::DENIED if the permission manager cannot be +// accessed for any reason. +blink::mojom::PermissionStatus GetBackgroundSyncPermissionOnUIThread( + const scoped_refptr& service_worker_context, + const GURL& origin) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BrowserContext* browser_context = + GetBrowserContextOnUIThread(service_worker_context); + if (!browser_context) + return blink::mojom::PermissionStatus::DENIED; + + PermissionManager* permission_manager = + browser_context->GetPermissionManager(); + if (!permission_manager) + return blink::mojom::PermissionStatus::DENIED; + + // The requesting origin always matches the embedding origin. + return permission_manager->GetPermissionStatus( + PermissionType::BACKGROUND_SYNC, origin, origin); } void NotifyBackgroundSyncRegisteredOnUIThread( @@ -126,11 +149,12 @@ void OnSyncEventFinished( const scoped_refptr& active_version, int request_id, const ServiceWorkerVersion::StatusCallback& callback, - ServiceWorkerEventStatus status) { - TRACE_EVENT1("ServiceWorker", "BackgroundSyncManager::OnSyncEventFinished", - "Request id", request_id); - if (!active_version->FinishRequest(request_id)) + mojom::ServiceWorkerEventStatus status) { + if (!active_version->FinishRequest( + request_id, + status == content::mojom::ServiceWorkerEventStatus::COMPLETED)) { return; + } callback.Run(mojo::ConvertTo(status)); } @@ -141,6 +165,9 @@ BackgroundSyncManager::BackgroundSyncRegistrations:: : next_id(BackgroundSyncRegistration::kInitialId) { } +BackgroundSyncManager::BackgroundSyncRegistrations::BackgroundSyncRegistrations( + const BackgroundSyncRegistrations& other) = default; + BackgroundSyncManager::BackgroundSyncRegistrations:: ~BackgroundSyncRegistrations() { } @@ -162,74 +189,25 @@ BackgroundSyncManager::~BackgroundSyncManager() { service_worker_context_->RemoveObserver(this); } -BackgroundSyncManager::RegistrationKey::RegistrationKey( - const BackgroundSyncRegistration& registration) - : RegistrationKey(registration.options()->tag, - registration.options()->periodicity) { -} - -BackgroundSyncManager::RegistrationKey::RegistrationKey( - const BackgroundSyncRegistrationOptions& options) - : RegistrationKey(options.tag, options.periodicity) { -} - -BackgroundSyncManager::RegistrationKey::RegistrationKey( - const std::string& tag, - SyncPeriodicity periodicity) - : value_(periodicity == SYNC_ONE_SHOT ? "o_" + tag : "p_" + tag) { -} - void BackgroundSyncManager::Register( int64_t sw_registration_id, const BackgroundSyncRegistrationOptions& options, - bool requested_from_service_worker, const StatusAndRegistrationCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (disabled_) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - PostErrorResponse(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); - return; - } - - if (requested_from_service_worker) { - op_scheduler_.ScheduleOperation( - base::Bind(&BackgroundSyncManager::RegisterCheckIfHasMainFrame, - weak_ptr_factory_.GetWeakPtr(), sw_registration_id, options, - MakeStatusAndRegistrationCompletion(callback))); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); return; } op_scheduler_.ScheduleOperation( - base::Bind(&BackgroundSyncManager::RegisterImpl, + base::Bind(&BackgroundSyncManager::RegisterCheckIfHasMainFrame, weak_ptr_factory_.GetWeakPtr(), sw_registration_id, options, MakeStatusAndRegistrationCompletion(callback))); } -void BackgroundSyncManager::GetRegistration( - int64_t sw_registration_id, - const std::string& sync_registration_tag, - SyncPeriodicity periodicity, - const StatusAndRegistrationCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (disabled_) { - PostErrorResponse(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); - return; - } - - RegistrationKey registration_key(sync_registration_tag, periodicity); - - op_scheduler_.ScheduleOperation(base::Bind( - &BackgroundSyncManager::GetRegistrationImpl, - weak_ptr_factory_.GetWeakPtr(), sw_registration_id, registration_key, - MakeStatusAndRegistrationCompletion(callback))); -} - void BackgroundSyncManager::GetRegistrations( int64_t sw_registration_id, - SyncPeriodicity periodicity, const StatusAndRegistrationsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -238,30 +216,15 @@ void BackgroundSyncManager::GetRegistrations( FROM_HERE, base::Bind( callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, - base::Passed( - scoped_ptr>( - new ScopedVector())))); + base::Passed(scoped_ptr>( + new ScopedVector())))); return; } op_scheduler_.ScheduleOperation( base::Bind(&BackgroundSyncManager::GetRegistrationsImpl, weak_ptr_factory_.GetWeakPtr(), sw_registration_id, - periodicity, MakeStatusAndRegistrationsCompletion(callback))); -} - -// Given a HandleId |handle_id|, return a new handle for the same -// registration. -scoped_ptr -BackgroundSyncManager::DuplicateRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - scoped_refptr* ref_registration = - registration_handle_ids_.Lookup(handle_id); - if (!ref_registration) - return scoped_ptr(); - return CreateRegistrationHandle(ref_registration->get()); + MakeStatusAndRegistrationsCompletion(callback))); } void BackgroundSyncManager::OnRegistrationDeleted(int64_t sw_registration_id, @@ -316,8 +279,6 @@ BackgroundSyncManager::BackgroundSyncManager( base::Bind(&BackgroundSyncManager::OnNetworkChanged, weak_ptr_factory_.GetWeakPtr()))); #endif - power_observer_.reset(new BackgroundSyncPowerObserver(base::Bind( - &BackgroundSyncManager::OnPowerChanged, weak_ptr_factory_.GetWeakPtr()))); } void BackgroundSyncManager::Init() { @@ -400,20 +361,12 @@ void BackgroundSyncManager::InitDidGetDataFromBackend( break; } - RegistrationKey registration_key(registration_proto.tag(), - registration_proto.periodicity()); - - scoped_refptr ref_registration( - new RefCountedRegistration()); - registrations->registration_map[registration_key] = ref_registration; - BackgroundSyncRegistration* registration = ref_registration->value(); + BackgroundSyncRegistration* registration = + ®istrations->registration_map[registration_proto.tag()]; BackgroundSyncRegistrationOptions* options = registration->options(); options->tag = registration_proto.tag(); - options->periodicity = registration_proto.periodicity(); - options->min_period = registration_proto.min_period(); options->network_state = registration_proto.network_state(); - options->power_state = registration_proto.power_state(); registration->set_id(registration_proto.id()); registration->set_num_attempts(registration_proto.num_attempts()); @@ -447,9 +400,8 @@ void BackgroundSyncManager::RegisterCheckIfHasMainFrame( ServiceWorkerRegistration* sw_registration = service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER); - PostErrorResponse(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + callback); return; } @@ -468,9 +420,7 @@ void BackgroundSyncManager::RegisterDidCheckIfMainFrame( DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!has_main_frame_client) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_NOT_ALLOWED); - PostErrorResponse(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, callback); return; } RegisterImpl(sw_registration_id, options, callback); @@ -483,25 +433,53 @@ void BackgroundSyncManager::RegisterImpl( DCHECK_CURRENTLY_ON(BrowserThread::IO); if (disabled_) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - PostErrorResponse(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); return; } if (options.tag.length() > kMaxTagLength) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_NOT_ALLOWED); - PostErrorResponse(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, callback); return; } ServiceWorkerRegistration* sw_registration = service_worker_context_->GetLiveRegistration(sw_registration_id); if (!sw_registration || !sw_registration->active_version()) { - BackgroundSyncMetrics::CountRegisterFailure( - options.periodicity, BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER); - PostErrorResponse(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + callback); + return; + } + + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, FROM_HERE, + base::Bind(&GetBackgroundSyncPermissionOnUIThread, + service_worker_context_, + sw_registration->pattern().GetOrigin()), + base::Bind(&BackgroundSyncManager::RegisterDidAskForPermission, + weak_ptr_factory_.GetWeakPtr(), sw_registration_id, options, + callback)); +} + +void BackgroundSyncManager::RegisterDidAskForPermission( + int64_t sw_registration_id, + const BackgroundSyncRegistrationOptions& options, + const StatusAndRegistrationCallback& callback, + blink::mojom::PermissionStatus permission_status) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (permission_status == blink::mojom::PermissionStatus::DENIED) { + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_PERMISSION_DENIED, + callback); + return; + } + DCHECK(permission_status == blink::mojom::PermissionStatus::GRANTED); + + ServiceWorkerRegistration* sw_registration = + service_worker_context_->GetLiveRegistration(sw_registration_id); + if (!sw_registration || !sw_registration->active_version()) { + // The service worker was shut down in the interim. + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, + callback); return; } @@ -510,57 +488,49 @@ void BackgroundSyncManager::RegisterImpl( service_worker_context_, sw_registration->pattern().GetOrigin())); - RefCountedRegistration* existing_registration_ref = - LookupActiveRegistration(sw_registration_id, RegistrationKey(options)); - if (existing_registration_ref) { - if (existing_registration_ref->value()->options()->Equals(options)) { - BackgroundSyncRegistration* existing_registration = - existing_registration_ref->value(); - - BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire = - AreOptionConditionsMet(options) - ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE - : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; - BackgroundSyncMetrics::CountRegisterSuccess( - existing_registration->options()->periodicity, - registration_could_fire, - BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); + BackgroundSyncRegistration* existing_registration = + LookupActiveRegistration(sw_registration_id, options.tag); + if (existing_registration) { + DCHECK(existing_registration->options()->Equals(options)); + + BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire = + AreOptionConditionsMet(options) + ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE + : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; + BackgroundSyncMetrics::CountRegisterSuccess( + registration_could_fire, + BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); if (existing_registration->IsFiring()) { existing_registration->set_sync_state( - BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING); + mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind( - callback, BACKGROUND_SYNC_STATUS_OK, - base::Passed(CreateRegistrationHandle(existing_registration_ref)))); + base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, + base::Passed(make_scoped_ptr(new BackgroundSyncRegistration( + *existing_registration))))); return; - } else { - existing_registration_ref->value()->SetUnregisteredState(); - } } - scoped_refptr new_ref_registration( - new RefCountedRegistration()); - BackgroundSyncRegistration* new_registration = new_ref_registration->value(); + BackgroundSyncRegistration new_registration; - *new_registration->options() = options; + *new_registration.options() = options; BackgroundSyncRegistrations* registrations = &active_registrations_[sw_registration_id]; - new_registration->set_id(registrations->next_id++); + new_registration.set_id(registrations->next_id++); AddActiveRegistration(sw_registration_id, sw_registration->pattern().GetOrigin(), - new_ref_registration); + new_registration); StoreRegistrations( sw_registration_id, base::Bind(&BackgroundSyncManager::RegisterDidStore, weak_ptr_factory_.GetWeakPtr(), sw_registration_id, - new_ref_registration, callback)); + new_registration, callback)); } void BackgroundSyncManager::DisableAndClearManager( @@ -575,15 +545,6 @@ void BackgroundSyncManager::DisableAndClearManager( disabled_ = true; - for (auto& sw_id_and_registrations : active_registrations_) { - for (auto& key_and_registration : - sw_id_and_registrations.second.registration_map) { - BackgroundSyncRegistration* registration = - key_and_registration.second->value(); - registration->SetUnregisteredState(); - } - } - active_registrations_.clear(); // Delete all backend entries. The memory representation of registered syncs @@ -628,10 +589,9 @@ void BackgroundSyncManager::DisableAndClearManagerClearedOne( base::Bind(barrier_closure)); } -BackgroundSyncManager::RefCountedRegistration* -BackgroundSyncManager::LookupActiveRegistration( +BackgroundSyncRegistration* BackgroundSyncManager::LookupActiveRegistration( int64_t sw_registration_id, - const RegistrationKey& registration_key) { + const std::string& tag) { DCHECK_CURRENTLY_ON(BrowserThread::IO); SWIdToRegistrationsMap::iterator it = @@ -643,12 +603,11 @@ BackgroundSyncManager::LookupActiveRegistration( DCHECK_LE(BackgroundSyncRegistration::kInitialId, registrations.next_id); DCHECK(!registrations.origin.is_empty()); - auto key_and_registration_iter = - registrations.registration_map.find(registration_key); + auto key_and_registration_iter = registrations.registration_map.find(tag); if (key_and_registration_iter == registrations.registration_map.end()) return nullptr; - return key_and_registration_iter->second.get(); + return &key_and_registration_iter->second; } void BackgroundSyncManager::StoreRegistrations( @@ -665,16 +624,13 @@ void BackgroundSyncManager::StoreRegistrations( for (const auto& key_and_registration : registrations.registration_map) { const BackgroundSyncRegistration& registration = - *key_and_registration.second->value(); + key_and_registration.second; BackgroundSyncRegistrationProto* registration_proto = registrations_proto.add_registration(); registration_proto->set_id(registration.id()); registration_proto->set_tag(registration.options()->tag); - registration_proto->set_periodicity(registration.options()->periodicity); - registration_proto->set_min_period(registration.options()->min_period); registration_proto->set_network_state( registration.options()->network_state); - registration_proto->set_power_state(registration.options()->power_state); registration_proto->set_num_attempts(registration.num_attempts()); registration_proto->set_delay_until( registration.delay_until().ToInternalValue()); @@ -689,21 +645,15 @@ void BackgroundSyncManager::StoreRegistrations( void BackgroundSyncManager::RegisterDidStore( int64_t sw_registration_id, - const scoped_refptr& new_registration_ref, + const BackgroundSyncRegistration& new_registration, const StatusAndRegistrationCallback& callback, ServiceWorkerStatusCode status) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - const BackgroundSyncRegistration* new_registration = - new_registration_ref->value(); - if (status == SERVICE_WORKER_ERROR_NOT_FOUND) { // The service worker registration is gone. - BackgroundSyncMetrics::CountRegisterFailure( - new_registration->options()->periodicity, - BACKGROUND_SYNC_STATUS_STORAGE_ERROR); active_registrations_.erase(sw_registration_id); - PostErrorResponse(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); + RecordFailureAndPostError(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); return; } @@ -711,55 +661,54 @@ void BackgroundSyncManager::RegisterDidStore( LOG(ERROR) << "BackgroundSync failed to store registration due to backend " "failure."; BackgroundSyncMetrics::CountRegisterFailure( - new_registration->options()->periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - DisableAndClearManager(base::Bind( - callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, - base::Passed(scoped_ptr()))); + DisableAndClearManager( + base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, + base::Passed(scoped_ptr()))); return; } BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire = - AreOptionConditionsMet(*new_registration->options()) + AreOptionConditionsMet(*new_registration.options()) ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; BackgroundSyncMetrics::CountRegisterSuccess( - new_registration->options()->periodicity, registration_could_fire, + registration_could_fire, BackgroundSyncMetrics::REGISTRATION_IS_NOT_DUPLICATE); FireReadyEvents(); + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind( - callback, BACKGROUND_SYNC_STATUS_OK, - base::Passed(CreateRegistrationHandle(new_registration_ref.get())))); + base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, + base::Passed(make_scoped_ptr( + new BackgroundSyncRegistration(new_registration))))); } -void BackgroundSyncManager::RemoveActiveRegistration( - int64_t sw_registration_id, - const RegistrationKey& registration_key) { +void BackgroundSyncManager::RemoveActiveRegistration(int64_t sw_registration_id, + const std::string& tag) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(LookupActiveRegistration(sw_registration_id, registration_key)); + DCHECK(LookupActiveRegistration(sw_registration_id, tag)); BackgroundSyncRegistrations* registrations = &active_registrations_[sw_registration_id]; - registrations->registration_map.erase(registration_key); + registrations->registration_map.erase(tag); } void BackgroundSyncManager::AddActiveRegistration( int64_t sw_registration_id, const GURL& origin, - const scoped_refptr& sync_registration) { + const BackgroundSyncRegistration& sync_registration) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(sync_registration->value()->IsValid()); + DCHECK(sync_registration.IsValid()); BackgroundSyncRegistrations* registrations = &active_registrations_[sw_registration_id]; registrations->origin = origin; - RegistrationKey registration_key(*sync_registration->value()); - registrations->registration_map[registration_key] = sync_registration; + registrations->registration_map[sync_registration.options()->tag] = + sync_registration; } void BackgroundSyncManager::StoreDataInBackend( @@ -784,19 +733,21 @@ void BackgroundSyncManager::GetDataFromBackend( callback); } -void BackgroundSyncManager::FireOneShotSync( - BackgroundSyncRegistrationHandle::HandleId handle_id, +void BackgroundSyncManager::DispatchSyncEvent( + const std::string& tag, const scoped_refptr& active_version, - BackgroundSyncEventLastChance last_chance, + mojom::BackgroundSyncEventLastChance last_chance, const ServiceWorkerVersion::StatusCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(active_version); if (active_version->running_status() != ServiceWorkerVersion::RUNNING) { active_version->RunAfterStartWorker( - callback, base::Bind(&BackgroundSyncManager::FireOneShotSync, - weak_ptr_factory_.GetWeakPtr(), handle_id, - active_version, last_chance, callback)); + ServiceWorkerMetrics::EventType::SYNC, + base::Bind(&BackgroundSyncManager::DispatchSyncEvent, + weak_ptr_factory_.GetWeakPtr(), tag, active_version, + last_chance, callback), + callback); return; } @@ -804,16 +755,13 @@ void BackgroundSyncManager::FireOneShotSync( ServiceWorkerMetrics::EventType::SYNC, callback, parameters_->max_sync_event_duration, ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); - base::WeakPtr client = - active_version->GetMojoServiceForRequest( - request_id); - - // The ServiceWorkerVersion doesn't know when the client (javascript) is done - // with the registration so don't give it a BackgroundSyncRegistrationHandle. - // Once the render process gets the handle_id it can create its own handle - // (with a new unique handle id). + base::WeakPtr client = + active_version + ->GetMojoServiceForRequest( + request_id); + client->Sync( - handle_id, last_chance, + tag, last_chance, base::Bind(&OnSyncEventFinished, active_version, request_id, callback)); } @@ -829,221 +777,13 @@ void BackgroundSyncManager::HasMainFrameProviderHost( service_worker_context_->HasMainFrameProviderHost(origin, callback); } -scoped_ptr -BackgroundSyncManager::CreateRegistrationHandle( - const scoped_refptr& registration) { - scoped_refptr* ptr = - new scoped_refptr(registration); - - // Registration handles have unique handle ids. The handle id maps to an - // internal RefCountedRegistration (which has the persistent registration id) - // via - // registration_reference_ids_. - BackgroundSyncRegistrationHandle::HandleId handle_id = - registration_handle_ids_.Add(ptr); - - return make_scoped_ptr(new BackgroundSyncRegistrationHandle( - weak_ptr_factory_.GetWeakPtr(), handle_id)); -} - -BackgroundSyncRegistration* BackgroundSyncManager::GetRegistrationForHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id) const { - scoped_refptr* ref_registration = - registration_handle_ids_.Lookup(handle_id); - if (!ref_registration) - return nullptr; - return (*ref_registration)->value(); -} - -void BackgroundSyncManager::ReleaseRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(registration_handle_ids_.Lookup(handle_id)); - registration_handle_ids_.Remove(handle_id); -} - -void BackgroundSyncManager::Unregister( - int64_t sw_registration_id, - BackgroundSyncRegistrationHandle::HandleId handle_id, - const StatusCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - BackgroundSyncRegistration* registration = - GetRegistrationForHandle(handle_id); - DCHECK(registration); - - if (disabled_) { - BackgroundSyncMetrics::CountUnregister( - registration->options()->periodicity, - BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR)); - return; - } - - op_scheduler_.ScheduleOperation(base::Bind( - &BackgroundSyncManager::UnregisterImpl, weak_ptr_factory_.GetWeakPtr(), - sw_registration_id, RegistrationKey(*registration), registration->id(), - registration->options()->periodicity, MakeStatusCompletion(callback))); -} - -void BackgroundSyncManager::UnregisterImpl( - int64_t sw_registration_id, - const RegistrationKey& registration_key, - BackgroundSyncRegistration::RegistrationId sync_registration_id, - SyncPeriodicity periodicity, - const StatusCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (disabled_) { - BackgroundSyncMetrics::CountUnregister( - periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR)); - return; - } - - RefCountedRegistration* existing_registration = - LookupActiveRegistration(sw_registration_id, registration_key); - - if (!existing_registration || - existing_registration->value()->id() != sync_registration_id) { - BackgroundSyncMetrics::CountUnregister(periodicity, - BACKGROUND_SYNC_STATUS_NOT_FOUND); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_NOT_FOUND)); - return; - } - - existing_registration->value()->SetUnregisteredState(); - - RemoveActiveRegistration(sw_registration_id, registration_key); - - StoreRegistrations(sw_registration_id, - base::Bind(&BackgroundSyncManager::UnregisterDidStore, - weak_ptr_factory_.GetWeakPtr(), - sw_registration_id, periodicity, callback)); -} - -void BackgroundSyncManager::UnregisterDidStore(int64_t sw_registration_id, - SyncPeriodicity periodicity, - const StatusCallback& callback, - ServiceWorkerStatusCode status) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (status == SERVICE_WORKER_ERROR_NOT_FOUND) { - // ServiceWorker was unregistered. - BackgroundSyncMetrics::CountUnregister( - periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - active_registrations_.erase(sw_registration_id); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR)); - return; - } - - if (status != SERVICE_WORKER_OK) { - LOG(ERROR) << "BackgroundSync failed to unregister due to backend failure."; - BackgroundSyncMetrics::CountUnregister( - periodicity, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); - DisableAndClearManager( - base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR)); - return; - } - - BackgroundSyncMetrics::CountUnregister(periodicity, - BACKGROUND_SYNC_STATUS_OK); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_OK)); -} - -void BackgroundSyncManager::NotifyWhenFinished( - BackgroundSyncRegistrationHandle::HandleId handle_id, - const StatusAndStateCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (disabled_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, - BACKGROUND_SYNC_STATE_FAILED)); - return; - } - - scoped_ptr registration_handle = - DuplicateRegistrationHandle(handle_id); - - op_scheduler_.ScheduleOperation( - base::Bind(&BackgroundSyncManager::NotifyWhenFinishedImpl, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(std::move(registration_handle)), callback)); -} - -void BackgroundSyncManager::NotifyWhenFinishedImpl( - scoped_ptr registration_handle, - const StatusAndStateCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK_EQ(SYNC_ONE_SHOT, registration_handle->options()->periodicity); - - if (disabled_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_STORAGE_ERROR, - BACKGROUND_SYNC_STATE_FAILED)); - return; - } - - if (!registration_handle->registration()->HasCompleted()) { - registration_handle->registration()->AddFinishedCallback( - base::Bind(&BackgroundSyncManager::NotifyWhenFinishedInvokeCallback, - weak_ptr_factory_.GetWeakPtr(), callback)); - op_scheduler_.CompleteOperationAndRunNext(); - return; - } - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, - registration_handle->sync_state())); - op_scheduler_.CompleteOperationAndRunNext(); -} - -void BackgroundSyncManager::NotifyWhenFinishedInvokeCallback( - const StatusAndStateCallback& callback, - BackgroundSyncState sync_state) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - callback.Run(BACKGROUND_SYNC_STATUS_OK, sync_state); -} - -void BackgroundSyncManager::GetRegistrationImpl( - int64_t sw_registration_id, - const RegistrationKey& registration_key, - const StatusAndRegistrationCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (disabled_) { - PostErrorResponse(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback); - return; - } - - RefCountedRegistration* registration = - LookupActiveRegistration(sw_registration_id, registration_key); - if (!registration) { - PostErrorResponse(BACKGROUND_SYNC_STATUS_NOT_FOUND, callback); - return; - } - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, - base::Passed(CreateRegistrationHandle(registration)))); -} - void BackgroundSyncManager::GetRegistrationsImpl( int64_t sw_registration_id, - SyncPeriodicity periodicity, const StatusAndRegistrationsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - scoped_ptr> out_registrations( - new ScopedVector()); + scoped_ptr> out_registrations( + new ScopedVector()); if (disabled_) { base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -1058,11 +798,11 @@ void BackgroundSyncManager::GetRegistrationsImpl( if (it != active_registrations_.end()) { const BackgroundSyncRegistrations& registrations = it->second; for (const auto& tag_and_registration : registrations.registration_map) { - RefCountedRegistration* registration = tag_and_registration.second.get(); - if (registration->value()->options()->periodicity == periodicity) { - out_registrations->push_back( - CreateRegistrationHandle(registration).release()); - } + const BackgroundSyncRegistration& registration = + tag_and_registration.second; + BackgroundSyncRegistration* out_registration = + new BackgroundSyncRegistration(registration); + out_registrations->push_back(out_registration); } } @@ -1074,26 +814,19 @@ void BackgroundSyncManager::GetRegistrationsImpl( bool BackgroundSyncManager::AreOptionConditionsMet( const BackgroundSyncRegistrationOptions& options) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - return network_observer_->NetworkSufficient(options.network_state) && - power_observer_->PowerSufficient(options.power_state); + return network_observer_->NetworkSufficient(options.network_state); } bool BackgroundSyncManager::IsRegistrationReadyToFire( const BackgroundSyncRegistration& registration) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // TODO(jkarlin): Add support for firing periodic registrations. - if (registration.options()->periodicity == SYNC_PERIODIC) - return false; - - if (registration.sync_state() != BACKGROUND_SYNC_STATE_PENDING) + if (registration.sync_state() != mojom::BackgroundSyncState::PENDING) return false; if (clock_->Now() < registration.delay_until()) return false; - DCHECK_EQ(SYNC_ONE_SHOT, registration.options()->periodicity); - return AreOptionConditionsMet(*registration.options()); } @@ -1105,20 +838,15 @@ void BackgroundSyncManager::RunInBackgroundIfNecessary() { for (const auto& key_and_registration : sw_id_and_registrations.second.registration_map) { const BackgroundSyncRegistration& registration = - *key_and_registration.second->value(); - if (registration.sync_state() == BACKGROUND_SYNC_STATE_PENDING) { - if (registration.options()->periodicity == SYNC_ONE_SHOT) { - if (clock_->Now() >= registration.delay_until()) { - soonest_wakeup_delta = base::TimeDelta(); - } else { - base::TimeDelta delay_delta = - registration.delay_until() - clock_->Now(); - if (delay_delta < soonest_wakeup_delta) - soonest_wakeup_delta = delay_delta; - } + key_and_registration.second; + if (registration.sync_state() == mojom::BackgroundSyncState::PENDING) { + if (clock_->Now() >= registration.delay_until()) { + soonest_wakeup_delta = base::TimeDelta(); } else { - // TODO(jkarlin): Support keeping the browser alive for periodic - // syncs. + base::TimeDelta delay_delta = + registration.delay_until() - clock_->Now(); + if (delay_delta < soonest_wakeup_delta) + soonest_wakeup_delta = delay_delta; } } } @@ -1170,26 +898,25 @@ void BackgroundSyncManager::FireReadyEventsImpl(const base::Closure& callback) { } // Find the registrations that are ready to run. - std::vector> sw_id_and_keys_to_fire; + std::vector> sw_id_and_tags_to_fire; for (auto& sw_id_and_registrations : active_registrations_) { const int64_t service_worker_id = sw_id_and_registrations.first; for (auto& key_and_registration : sw_id_and_registrations.second.registration_map) { - BackgroundSyncRegistration* registration = - key_and_registration.second->value(); + BackgroundSyncRegistration* registration = &key_and_registration.second; if (IsRegistrationReadyToFire(*registration)) { - sw_id_and_keys_to_fire.push_back( + sw_id_and_tags_to_fire.push_back( std::make_pair(service_worker_id, key_and_registration.first)); // The state change is not saved to persistent storage because // if the sync event is killed mid-sync then it should return to // SYNC_STATE_PENDING. - registration->set_sync_state(BACKGROUND_SYNC_STATE_FIRING); + registration->set_sync_state(mojom::BackgroundSyncState::FIRING); } } } - if (sw_id_and_keys_to_fire.empty()) { + if (sw_id_and_tags_to_fire.empty()) { RunInBackgroundIfNecessary(); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(callback)); @@ -1201,33 +928,33 @@ void BackgroundSyncManager::FireReadyEventsImpl(const base::Closure& callback) { // Fire the sync event of the ready registrations and run |callback| once // they're all done. base::Closure events_fired_barrier_closure = base::BarrierClosure( - sw_id_and_keys_to_fire.size(), + sw_id_and_tags_to_fire.size(), base::Bind(&BackgroundSyncManager::FireReadyEventsAllEventsFiring, weak_ptr_factory_.GetWeakPtr(), callback)); // Record the total time taken after all events have run to completion. base::Closure events_completed_barrier_closure = - base::BarrierClosure(sw_id_and_keys_to_fire.size(), + base::BarrierClosure(sw_id_and_tags_to_fire.size(), base::Bind(&OnAllSyncEventsCompleted, start_time, - sw_id_and_keys_to_fire.size())); + sw_id_and_tags_to_fire.size())); - for (const auto& sw_id_and_key : sw_id_and_keys_to_fire) { - int64_t service_worker_id = sw_id_and_key.first; - const RefCountedRegistration* registration = - LookupActiveRegistration(service_worker_id, sw_id_and_key.second); + for (const auto& sw_id_and_tag : sw_id_and_tags_to_fire) { + int64_t service_worker_id = sw_id_and_tag.first; + const BackgroundSyncRegistration* registration = + LookupActiveRegistration(service_worker_id, sw_id_and_tag.second); DCHECK(registration); service_worker_context_->FindReadyRegistrationForId( service_worker_id, active_registrations_[service_worker_id].origin, base::Bind(&BackgroundSyncManager::FireReadyEventsDidFindRegistration, - weak_ptr_factory_.GetWeakPtr(), sw_id_and_key.second, - registration->value()->id(), events_fired_barrier_closure, + weak_ptr_factory_.GetWeakPtr(), sw_id_and_tag.second, + registration->id(), events_fired_barrier_closure, events_completed_barrier_closure)); } } void BackgroundSyncManager::FireReadyEventsDidFindRegistration( - const RegistrationKey& registration_key, + const std::string& tag, BackgroundSyncRegistration::RegistrationId registration_id, const base::Closure& event_fired_callback, const base::Closure& event_completed_callback, @@ -1243,37 +970,27 @@ void BackgroundSyncManager::FireReadyEventsDidFindRegistration( return; } - RefCountedRegistration* registration = LookupActiveRegistration( - service_worker_registration->id(), registration_key); + BackgroundSyncRegistration* registration = + LookupActiveRegistration(service_worker_registration->id(), tag); DCHECK(registration); - // Create a handle and keep it until the sync event completes. The client can - // acquire its own handle for longer-term use. - scoped_ptr registration_handle = - CreateRegistrationHandle(registration); - num_firing_registrations_ += 1; - BackgroundSyncRegistrationHandle::HandleId handle_id = - registration_handle->handle_id(); - - BackgroundSyncEventLastChance last_chance = - registration->value()->num_attempts() == - parameters_->max_sync_attempts - 1 - ? BACKGROUND_SYNC_EVENT_LAST_CHANCE_IS_LAST_CHANCE - : BACKGROUND_SYNC_EVENT_LAST_CHANCE_IS_NOT_LAST_CHANCE; + mojom::BackgroundSyncEventLastChance last_chance = + registration->num_attempts() == parameters_->max_sync_attempts - 1 + ? mojom::BackgroundSyncEventLastChance::IS_LAST_CHANCE + : mojom::BackgroundSyncEventLastChance::IS_NOT_LAST_CHANCE; HasMainFrameProviderHost( service_worker_registration->pattern().GetOrigin(), - base::Bind(&BackgroundSyncMetrics::RecordEventStarted, - registration->value()->options()->periodicity)); + base::Bind(&BackgroundSyncMetrics::RecordEventStarted)); - FireOneShotSync( - handle_id, service_worker_registration->active_version(), last_chance, + DispatchSyncEvent( + registration->options()->tag, + service_worker_registration->active_version(), last_chance, base::Bind(&BackgroundSyncManager::EventComplete, weak_ptr_factory_.GetWeakPtr(), service_worker_registration, - service_worker_registration->id(), - base::Passed(std::move(registration_handle)), + service_worker_registration->id(), tag, event_completed_callback)); base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -1294,34 +1011,48 @@ void BackgroundSyncManager::FireReadyEventsAllEventsFiring( void BackgroundSyncManager::EventComplete( const scoped_refptr& service_worker_registration, int64_t service_worker_id, - scoped_ptr registration_handle, + const std::string& tag, const base::Closure& callback, ServiceWorkerStatusCode status_code) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // Do not check for disabled as events that were firing when disabled should - // be allowed to complete (for NotifyWhenFinished). + if (disabled_) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback)); + return; + } + op_scheduler_.ScheduleOperation(base::Bind( &BackgroundSyncManager::EventCompleteImpl, weak_ptr_factory_.GetWeakPtr(), - service_worker_id, base::Passed(std::move(registration_handle)), - status_code, MakeClosureCompletion(callback))); + service_worker_id, tag, status_code, MakeClosureCompletion(callback))); } void BackgroundSyncManager::EventCompleteImpl( int64_t service_worker_id, - scoped_ptr registration_handle, + const std::string& tag, ServiceWorkerStatusCode status_code, const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (disabled_) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback)); + return; + } + + num_firing_registrations_ -= 1; + BackgroundSyncRegistration* registration = - registration_handle->registration(); - DCHECK(registration); - DCHECK(!registration->HasCompleted()); + LookupActiveRegistration(service_worker_id, tag); + if (!registration) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback)); + return; + } - registration->set_num_attempts(registration->num_attempts() + 1); + DCHECK_NE(mojom::BackgroundSyncState::PENDING, registration->sync_state()); - num_firing_registrations_ -= 1; + registration->set_num_attempts(registration->num_attempts() + 1); // The event ran to completion, we should count it, no matter what happens // from here. @@ -1331,58 +1062,36 @@ void BackgroundSyncManager::EventCompleteImpl( HasMainFrameProviderHost( sw_registration->pattern().GetOrigin(), base::Bind(&BackgroundSyncMetrics::RecordEventResult, - registration->options()->periodicity, status_code == SERVICE_WORKER_OK)); } - if (registration->options()->periodicity == SYNC_ONE_SHOT) { - if (registration->sync_state() == - BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING) { - registration->set_sync_state(BACKGROUND_SYNC_STATE_PENDING); - registration->set_num_attempts(0); - } else if (status_code != SERVICE_WORKER_OK) { // Sync failed - bool can_retry = - registration->num_attempts() < parameters_->max_sync_attempts; - if (registration->sync_state() == - BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING) { - registration->set_sync_state(can_retry - ? BACKGROUND_SYNC_STATE_UNREGISTERED - : BACKGROUND_SYNC_STATE_FAILED); - registration->RunFinishedCallbacks(); - } else if (can_retry) { - registration->set_sync_state(BACKGROUND_SYNC_STATE_PENDING); - registration->set_delay_until( - clock_->Now() + - parameters_->initial_retry_delay * - pow(parameters_->retry_delay_factor, - registration->num_attempts() - 1)); - } else { - registration->set_sync_state(BACKGROUND_SYNC_STATE_FAILED); - registration->RunFinishedCallbacks(); - } - } else { // Sync succeeded - registration->set_sync_state(BACKGROUND_SYNC_STATE_SUCCESS); - registration->RunFinishedCallbacks(); - } - - if (registration->HasCompleted()) { - RegistrationKey key(*registration); - RefCountedRegistration* active_registration = - LookupActiveRegistration(service_worker_id, key); - if (active_registration && - active_registration->value()->id() == registration->id()) { - RemoveActiveRegistration(service_worker_id, key); - } - } - } else { // !SYNC_ONE_SHOT - // TODO(jkarlin): Add support for running periodic syncs. (crbug.com/479674) - NOTREACHED(); + bool registration_completed = true; + bool can_retry = + registration->num_attempts() < parameters_->max_sync_attempts; + + if (registration->sync_state() == + mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) { + registration->set_sync_state(mojom::BackgroundSyncState::PENDING); + registration->set_num_attempts(0); + registration_completed = false; + } else if (status_code != SERVICE_WORKER_OK && + can_retry) { // Sync failed but can retry + registration->set_sync_state(mojom::BackgroundSyncState::PENDING); + registration->set_delay_until(clock_->Now() + + parameters_->initial_retry_delay * + pow(parameters_->retry_delay_factor, + registration->num_attempts() - 1)); + registration_completed = false; } - if (disabled_) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback)); - return; + if (registration_completed) { + const std::string& tag = registration->options()->tag; + BackgroundSyncRegistration* active_registration = + LookupActiveRegistration(service_worker_id, tag); + if (active_registration && + active_registration->id() == registration->id()) { + RemoveActiveRegistration(service_worker_id, tag); + } } StoreRegistrations( @@ -1454,12 +1163,6 @@ void BackgroundSyncManager::OnNetworkChanged() { FireReadyEvents(); } -void BackgroundSyncManager::OnPowerChanged() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - FireReadyEvents(); -} - void BackgroundSyncManager::SetMaxSyncAttemptsImpl( int max_attempts, const base::Closure& callback) { @@ -1482,21 +1185,20 @@ void BackgroundSyncManager::CompleteOperationCallback(const CallbackT& callback, void BackgroundSyncManager::CompleteStatusAndRegistrationCallback( StatusAndRegistrationCallback callback, BackgroundSyncStatus status, - scoped_ptr registration_handle) { + scoped_ptr registration) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback.Run(status, std::move(registration_handle)); + callback.Run(status, std::move(registration)); op_scheduler_.CompleteOperationAndRunNext(); } void BackgroundSyncManager::CompleteStatusAndRegistrationsCallback( StatusAndRegistrationsCallback callback, BackgroundSyncStatus status, - scoped_ptr> - registration_handles) { + scoped_ptr> registrations) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback.Run(status, std::move(registration_handles)); + callback.Run(status, std::move(registrations)); op_scheduler_.CompleteOperationAndRunNext(); } diff --git a/chromium/content/browser/background_sync/background_sync_manager.h b/chromium/content/browser/background_sync/background_sync_manager.h index 42a69ea8989..e80ee0a485c 100644 --- a/chromium/content/browser/background_sync/background_sync_manager.h +++ b/chromium/content/browser/background_sync/background_sync_manager.h @@ -22,7 +22,6 @@ #include "base/time/clock.h" #include "content/browser/background_sync/background_sync.pb.h" #include "content/browser/background_sync/background_sync_registration.h" -#include "content/browser/background_sync/background_sync_registration_handle.h" #include "content/browser/background_sync/background_sync_status.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/browser/service_worker/service_worker_context_observer.h" @@ -32,12 +31,18 @@ #include "content/common/service_worker/service_worker_status_code.h" #include "content/public/browser/background_sync_parameters.h" #include "content/public/browser/browser_thread.h" +#include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h" #include "url/gurl.h" +namespace blink { +namespace mojom { +enum class PermissionStatus; +} +} + namespace content { class BackgroundSyncNetworkObserver; -class BackgroundSyncPowerObserver; class ServiceWorkerContextWrapper; // BackgroundSyncManager manages and stores the set of background sync @@ -53,53 +58,32 @@ class CONTENT_EXPORT BackgroundSyncManager using StatusCallback = base::Callback; using StatusAndRegistrationCallback = base::Callback)>; - using StatusAndStateCallback = - base::Callback; + scoped_ptr)>; using StatusAndRegistrationsCallback = base::Callback>)>; + scoped_ptr>)>; static scoped_ptr Create( const scoped_refptr& service_worker_context); ~BackgroundSyncManager() override; // Stores the given background sync registration and adds it to the scheduling - // queue. It will overwrite an existing registration with the same tag and - // periodicity unless they're identical (save for the id). Calls |callback| - // with BACKGROUND_SYNC_STATUS_OK and the accepted registration on success. + // queue. It will overwrite an existing registration with the same tag unless + // they're identical (save for the id). Calls |callback| with + // BACKGROUND_SYNC_STATUS_OK and the accepted registration on success. // The accepted registration will have a unique id. It may also have altered // parameters if the user or UA chose different parameters than those // supplied. void Register(int64_t sw_registration_id, const BackgroundSyncRegistrationOptions& options, - bool requested_from_service_worker, const StatusAndRegistrationCallback& callback); - // Finds the background sync registration associated with - // |sw_registration_id|, periodicity |periodicity|, and tag - // |sync_registration_tag|. Calls |callback| with - // BACKGROUND_SYNC_STATUS_NOT_FOUND if it doesn't exist. Calls |callback| with - // BACKGROUND_SYNC_STATUS_OK on success. If the callback's status - // is not BACKGROUND_SYNC_STATUS_OK then the callback's RegistrationHandle - // will be nullptr. - void GetRegistration(int64_t sw_registration_id, - const std::string& sync_registration_tag, - SyncPeriodicity periodicity, - const StatusAndRegistrationCallback& callback); - // Finds the background sync registrations associated with - // |sw_registration_id| and periodicity |periodicity|. Calls - // |callback| with BACKGROUND_SYNC_STATUS_OK on success. + // |sw_registration_id|. Calls |callback| with BACKGROUND_SYNC_STATUS_OK on + // success. void GetRegistrations(int64_t sw_registration_id, - SyncPeriodicity periodicity, const StatusAndRegistrationsCallback& callback); - // Given a HandleId |handle_id|, return a new handle for the same - // registration. - scoped_ptr DuplicateRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id); - // ServiceWorkerContextObserver overrides. void OnRegistrationDeleted(int64_t sw_registration_id, const GURL& pattern) override; @@ -119,14 +103,6 @@ class CONTENT_EXPORT BackgroundSyncManager } protected: - // A registration might be referenced by the client longer than - // the BackgroundSyncManager needs to keep track of it (e.g., the event has - // finished firing). The BackgroundSyncManager reference counts its - // registrations internally and every BackgroundSyncRegistrationHandle has a - // unique handle id which maps to a locally maintained (in - // client_registration_ids_) scoped_refptr. - class RefCountedRegistration; - explicit BackgroundSyncManager( const scoped_refptr& context); @@ -144,10 +120,10 @@ class CONTENT_EXPORT BackgroundSyncManager const std::string& backend_key, const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback& callback); - virtual void FireOneShotSync( - BackgroundSyncRegistrationHandle::HandleId handle_id, + virtual void DispatchSyncEvent( + const std::string& tag, const scoped_refptr& active_version, - BackgroundSyncEventLastChance last_chance, + mojom::BackgroundSyncEventLastChance last_chance, const ServiceWorkerVersion::StatusCallback& callback); virtual void ScheduleDelayedTask(const base::Closure& callback, base::TimeDelta delay); @@ -157,29 +133,12 @@ class CONTENT_EXPORT BackgroundSyncManager private: friend class TestBackgroundSyncManager; friend class BackgroundSyncManagerTest; - friend class BackgroundSyncRegistrationHandle; - - class RegistrationKey { - public: - explicit RegistrationKey(const BackgroundSyncRegistration& registration); - explicit RegistrationKey(const BackgroundSyncRegistrationOptions& options); - RegistrationKey(const std::string& tag, SyncPeriodicity periodicity); - RegistrationKey(const RegistrationKey& other) = default; - RegistrationKey& operator=(const RegistrationKey& other) = default; - - bool operator<(const RegistrationKey& rhs) const { - return value_ < rhs.value_; - } - - private: - std::string value_; - }; struct BackgroundSyncRegistrations { - using RegistrationMap = - std::map>; + using RegistrationMap = std::map; BackgroundSyncRegistrations(); + BackgroundSyncRegistrations(const BackgroundSyncRegistrations& other); ~BackgroundSyncRegistrations(); RegistrationMap registration_map; @@ -192,23 +151,8 @@ class CONTENT_EXPORT BackgroundSyncManager static const size_t kMaxTagLength = 10240; - scoped_ptr CreateRegistrationHandle( - const scoped_refptr& registration); - - // Returns the BackgroundSyncRegistration corresponding to |handle_id|. - // Returns nullptr if the registration is not found. - BackgroundSyncRegistration* GetRegistrationForHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id) const; - - // The BackgroundSyncManager holds references to registrations that have - // active Handles. The handles must call this on destruction. - void ReleaseRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id); - // Disable the manager. Already queued operations will abort once they start - // to run (in their impl methods). Future operations will not queue. The one - // exception is already firing events -- their responses will be processed in - // order to notify their final state. + // to run (in their impl methods). Future operations will not queue. // The list of active registrations is cleared and the backend is also cleared // (if it's still functioning). The manager will reenable itself once it // receives the OnStorageWiped message or on browser restart. @@ -221,9 +165,9 @@ class CONTENT_EXPORT BackgroundSyncManager ServiceWorkerStatusCode status); // Returns the existing registration or nullptr if it cannot be found. - RefCountedRegistration* LookupActiveRegistration( + BackgroundSyncRegistration* LookupActiveRegistration( int64_t sw_registration_id, - const RegistrationKey& registration_key); + const std::string& tag); // Write all registrations for a given |sw_registration_id| to persistent // storage. @@ -232,12 +176,12 @@ class CONTENT_EXPORT BackgroundSyncManager // Removes the active registration if it is in the map. void RemoveActiveRegistration(int64_t sw_registration_id, - const RegistrationKey& registration_key); + const std::string& tag); void AddActiveRegistration( int64_t sw_registration_id, const GURL& origin, - const scoped_refptr& sync_registration); + const BackgroundSyncRegistration& sync_registration); void InitImpl(const base::Closure& callback); void InitDidGetControllerParameters( @@ -261,49 +205,18 @@ class CONTENT_EXPORT BackgroundSyncManager void RegisterImpl(int64_t sw_registration_id, const BackgroundSyncRegistrationOptions& options, const StatusAndRegistrationCallback& callback); - void RegisterDidStore( + void RegisterDidAskForPermission( int64_t sw_registration_id, - const scoped_refptr& new_registration_ref, + const BackgroundSyncRegistrationOptions& options, const StatusAndRegistrationCallback& callback, - ServiceWorkerStatusCode status); - - // Removes the background sync with periodicity |periodicity| and id - // |sync_registration_id|. Calls |callback| with - // BACKGROUND_SYNC_STATUS_NOT_FOUND if no match is found. Calls |callback| - // with BACKGROUND_SYNC_STATUS_OK on success. - void Unregister(int64_t sw_registration_id, - BackgroundSyncRegistrationHandle::HandleId handle_id, - const StatusCallback& callback); - void UnregisterImpl( - int64_t sw_registration_id, - const RegistrationKey& key, - BackgroundSyncRegistration::RegistrationId sync_registration_id, - SyncPeriodicity periodicity, - const StatusCallback& callback); - void UnregisterDidStore(int64_t sw_registration_id, - SyncPeriodicity periodicity, - const StatusCallback& callback, - ServiceWorkerStatusCode status); - - // NotifyWhenFinished and its callbacks. See - // BackgroundSyncRegistrationHandle::NotifyWhenFinished for detailed - // documentation. - void NotifyWhenFinished(BackgroundSyncRegistrationHandle::HandleId handle_id, - const StatusAndStateCallback& callback); - void NotifyWhenFinishedImpl( - scoped_ptr registration_handle, - const StatusAndStateCallback& callback); - void NotifyWhenFinishedInvokeCallback(const StatusAndStateCallback& callback, - BackgroundSyncState status); - - // GetRegistration callbacks - void GetRegistrationImpl(int64_t sw_registration_id, - const RegistrationKey& registration_key, - const StatusAndRegistrationCallback& callback); + blink::mojom::PermissionStatus permission_status); + void RegisterDidStore(int64_t sw_registration_id, + const BackgroundSyncRegistration& new_registration, + const StatusAndRegistrationCallback& callback, + ServiceWorkerStatusCode status); // GetRegistrations callbacks void GetRegistrationsImpl(int64_t sw_registration_id, - SyncPeriodicity periodicity, const StatusAndRegistrationsCallback& callback); bool AreOptionConditionsMet(const BackgroundSyncRegistrationOptions& options); @@ -324,7 +237,7 @@ class CONTENT_EXPORT BackgroundSyncManager void FireReadyEvents(); void FireReadyEventsImpl(const base::Closure& callback); void FireReadyEventsDidFindRegistration( - const RegistrationKey& registration_key, + const std::string& tag, BackgroundSyncRegistration::RegistrationId registration_id, const base::Closure& event_fired_callback, const base::Closure& event_completed_callback, @@ -334,18 +247,16 @@ class CONTENT_EXPORT BackgroundSyncManager void FireReadyEventsAllEventsFiring(const base::Closure& callback); // Called when a sync event has completed. - void EventComplete( - const scoped_refptr& - service_worker_registration, - int64_t service_worker_id, - scoped_ptr registration_handle, - const base::Closure& callback, - ServiceWorkerStatusCode status_code); - void EventCompleteImpl( - int64_t service_worker_id, - scoped_ptr registration_handle, - ServiceWorkerStatusCode status_code, - const base::Closure& callback); + void EventComplete(const scoped_refptr& + service_worker_registration, + int64_t service_worker_id, + const std::string& tag, + const base::Closure& callback, + ServiceWorkerStatusCode status_code); + void EventCompleteImpl(int64_t service_worker_id, + const std::string& tag, + ServiceWorkerStatusCode status_code, + const base::Closure& callback); void EventCompleteDidStore(int64_t service_worker_id, const base::Closure& callback, ServiceWorkerStatusCode status_code); @@ -362,7 +273,6 @@ class CONTENT_EXPORT BackgroundSyncManager void OnStorageWipedImpl(const base::Closure& callback); void OnNetworkChanged(); - void OnPowerChanged(); // SetMaxSyncAttempts callback void SetMaxSyncAttemptsImpl(int max_sync_attempts, @@ -375,11 +285,11 @@ class CONTENT_EXPORT BackgroundSyncManager void CompleteStatusAndRegistrationCallback( StatusAndRegistrationCallback callback, BackgroundSyncStatus status, - scoped_ptr result); + scoped_ptr registration); void CompleteStatusAndRegistrationsCallback( StatusAndRegistrationsCallback callback, BackgroundSyncStatus status, - scoped_ptr> results); + scoped_ptr> registrations); base::Closure MakeEmptyCompletion(); base::Closure MakeClosureCompletion(const base::Closure& callback); StatusAndRegistrationCallback MakeStatusAndRegistrationCompletion( @@ -404,12 +314,6 @@ class CONTENT_EXPORT BackgroundSyncManager base::CancelableCallback delayed_sync_task_; scoped_ptr network_observer_; - scoped_ptr power_observer_; - - // The registrations that clients have handles to. - IDMap, - IDMapOwnPointer, - BackgroundSyncRegistrationHandle::HandleId> registration_handle_ids_; scoped_ptr clock_; diff --git a/chromium/content/browser/background_sync/background_sync_manager_unittest.cc b/chromium/content/browser/background_sync/background_sync_manager_unittest.cc index bf0670df243..dbe81cdfec8 100644 --- a/chromium/content/browser/background_sync/background_sync_manager_unittest.cc +++ b/chromium/content/browser/background_sync/background_sync_manager_unittest.cc @@ -11,16 +11,14 @@ #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" -#include "base/power_monitor/power_monitor.h" -#include "base/power_monitor/power_monitor_source.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/test/mock_entropy_provider.h" #include "base/test/simple_test_clock.h" #include "base/thread_task_runner_handle.h" #include "content/browser/background_sync/background_sync_network_observer.h" -#include "content/browser/background_sync/background_sync_registration_handle.h" #include "content/browser/background_sync/background_sync_status.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/service_worker/embedded_worker_test_helper.h" @@ -28,18 +26,26 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_storage.h" #include "content/browser/storage_partition_impl.h" -#include "content/public/browser/background_sync_controller.h" #include "content/public/browser/background_sync_parameters.h" +#include "content/public/browser/permission_type.h" #include "content/public/test/background_sync_test_util.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/mock_background_sync_controller.h" +#include "content/test/mock_permission_manager.h" +#include "content/test/test_background_sync_manager.h" #include "net/base/network_change_notifier.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h" namespace content { namespace { +using ::testing::Return; +using ::testing::_; + const char kPattern1[] = "https://example.com/a"; const char kPattern2[] = "https://example.com/b"; const char kScript1[] = "https://example.com/a/script.js"; @@ -69,7 +75,7 @@ void UnregisterServiceWorkerCallback(bool* called, *called = true; } -void OneShotSuccessfulCallback( +void DispatchSyncSuccessfulCallback( int* count, const scoped_refptr& active_version, const ServiceWorkerVersion::StatusCallback& callback) { @@ -77,7 +83,7 @@ void OneShotSuccessfulCallback( callback.Run(SERVICE_WORKER_OK); } -void OneShotFailedCallback( +void DispatchSyncFailedCallback( int* count, const scoped_refptr& active_version, const ServiceWorkerVersion::StatusCallback& callback) { @@ -85,7 +91,7 @@ void OneShotFailedCallback( callback.Run(SERVICE_WORKER_ERROR_FAILED); } -void OneShotDelayedCallback( +void DispatchSyncDelayedCallback( int* count, ServiceWorkerVersion::StatusCallback* out_callback, const scoped_refptr& active_version, @@ -94,222 +100,18 @@ void OneShotDelayedCallback( *out_callback = callback; } -void NotifyWhenFinishedCallback(bool* was_called, - BackgroundSyncStatus* out_status, - BackgroundSyncState* out_state, - BackgroundSyncStatus status, - BackgroundSyncState state) { - *was_called = true; - *out_status = status; - *out_state = state; -} - -class TestPowerSource : public base::PowerMonitorSource { - public: - void GeneratePowerStateEvent(bool on_battery_power) { - test_on_battery_power_ = on_battery_power; - ProcessPowerEvent(POWER_STATE_EVENT); - } - - private: - bool IsOnBatteryPowerImpl() final { return test_on_battery_power_; } - bool test_on_battery_power_ = false; -}; - -class TestBackgroundSyncController : public BackgroundSyncController { - public: - TestBackgroundSyncController() = default; - - // BackgroundSyncController Overrides - void NotifyBackgroundSyncRegistered(const GURL& origin) override { - registration_count_ += 1; - registration_origin_ = origin; - } - void RunInBackground(bool enabled, int64_t min_ms) override { - run_in_background_count_ += 1; - run_in_background_enabled_ = enabled; - run_in_background_min_ms_ = min_ms; - } - void GetParameterOverrides( - BackgroundSyncParameters* parameters) const override { - *parameters = background_sync_parameters_; - } - - int registration_count() const { return registration_count_; } - GURL registration_origin() const { return registration_origin_; } - int run_in_background_count() const { return run_in_background_count_; } - bool run_in_background_enabled() const { return run_in_background_enabled_; } - int64_t run_in_background_min_ms() const { return run_in_background_min_ms_; } - BackgroundSyncParameters* background_sync_parameters() { - return &background_sync_parameters_; - } - - private: - int registration_count_ = 0; - GURL registration_origin_; - - int run_in_background_count_ = 0; - bool run_in_background_enabled_ = true; - int64_t run_in_background_min_ms_ = 0; - BackgroundSyncParameters background_sync_parameters_; - - DISALLOW_COPY_AND_ASSIGN(TestBackgroundSyncController); -}; - } // namespace -// A BackgroundSyncManager that can simulate delaying and corrupting the backend -// storage and service worker onsync events. -class TestBackgroundSyncManager : public BackgroundSyncManager { - public: - using OneShotCallback = - base::Callback&, - const ServiceWorkerVersion::StatusCallback&)>; - - explicit TestBackgroundSyncManager( - const scoped_refptr& service_worker_context) - : BackgroundSyncManager(service_worker_context) { - } - - void DoInit() { Init(); } - - void StoreDataInBackendContinue( - int64_t sw_registration_id, - const GURL& origin, - const std::string& key, - const std::string& data, - const ServiceWorkerStorage::StatusCallback& callback) { - BackgroundSyncManager::StoreDataInBackend(sw_registration_id, origin, key, - data, callback); - } - - void GetDataFromBackendContinue( - const std::string& key, - const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback& - callback) { - BackgroundSyncManager::GetDataFromBackend(key, callback); - } - - void Continue() { - ASSERT_FALSE(continuation_.is_null()); - continuation_.Run(); - continuation_.Reset(); - } - - void ClearDelayedTask() { delayed_task_.Reset(); } - - void set_corrupt_backend(bool corrupt_backend) { - corrupt_backend_ = corrupt_backend; - } - void set_delay_backend(bool delay_backend) { delay_backend_ = delay_backend; } - void set_one_shot_callback(const OneShotCallback& callback) { - one_shot_callback_ = callback; - } - - base::Closure delayed_task() const { return delayed_task_; } - base::TimeDelta delayed_task_delta() const { return delayed_task_delta_; } - - BackgroundSyncEventLastChance last_chance() const { return last_chance_; } - - void set_has_main_frame_provider_host(bool value) { - has_main_frame_provider_host_ = value; - } - - const BackgroundSyncParameters* background_sync_parameters() const { - return parameters_.get(); - } - - protected: - void StoreDataInBackend( - int64_t sw_registration_id, - const GURL& origin, - const std::string& key, - const std::string& data, - const ServiceWorkerStorage::StatusCallback& callback) override { - EXPECT_TRUE(continuation_.is_null()); - if (corrupt_backend_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED)); - return; - } - continuation_ = - base::Bind(&TestBackgroundSyncManager::StoreDataInBackendContinue, - base::Unretained(this), sw_registration_id, origin, key, - data, callback); - if (delay_backend_) - return; - - Continue(); - } - - void GetDataFromBackend( - const std::string& key, - const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback& - callback) override { - EXPECT_TRUE(continuation_.is_null()); - if (corrupt_backend_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(callback, std::vector>(), - SERVICE_WORKER_ERROR_FAILED)); - return; - } - continuation_ = - base::Bind(&TestBackgroundSyncManager::GetDataFromBackendContinue, - base::Unretained(this), key, callback); - if (delay_backend_) - return; - - Continue(); - } - - void FireOneShotSync( - BackgroundSyncRegistrationHandle::HandleId handle_id, - const scoped_refptr& active_version, - BackgroundSyncEventLastChance last_chance, - const ServiceWorkerVersion::StatusCallback& callback) override { - ASSERT_FALSE(one_shot_callback_.is_null()); - last_chance_ = last_chance; - one_shot_callback_.Run(active_version, callback); - } - - void ScheduleDelayedTask(const base::Closure& callback, - base::TimeDelta delay) override { - delayed_task_ = callback; - delayed_task_delta_ = delay; - } - - void HasMainFrameProviderHost(const GURL& origin, - const BoolCallback& callback) override { - callback.Run(has_main_frame_provider_host_); - } - - private: - bool corrupt_backend_ = false; - bool delay_backend_ = false; - bool has_main_frame_provider_host_ = true; - BackgroundSyncEventLastChance last_chance_ = - BACKGROUND_SYNC_EVENT_LAST_CHANCE_IS_NOT_LAST_CHANCE; - base::Closure continuation_; - OneShotCallback one_shot_callback_; - base::Closure delayed_task_; - base::TimeDelta delayed_task_delta_; -}; - class BackgroundSyncManagerTest : public testing::Test { public: BackgroundSyncManagerTest() : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) { sync_options_1_.tag = "foo"; - sync_options_1_.periodicity = SYNC_ONE_SHOT; sync_options_1_.network_state = NETWORK_STATE_ONLINE; - sync_options_1_.power_state = POWER_STATE_AUTO; sync_options_2_.tag = "bar"; - sync_options_2_.periodicity = SYNC_ONE_SHOT; sync_options_2_.network_state = NETWORK_STATE_ONLINE; - sync_options_2_.power_state = POWER_STATE_AUTO; } void SetUp() override { @@ -321,28 +123,23 @@ class BackgroundSyncManagerTest : public testing::Test { // extra SW stuff. helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath())); + scoped_ptr mock_permission_manager( + new testing::NiceMock()); + ON_CALL(*mock_permission_manager, + GetPermissionStatus(PermissionType::BACKGROUND_SYNC, _, _)) + .WillByDefault(Return(blink::mojom::PermissionStatus::GRANTED)); + helper_->browser_context()->SetPermissionManager( + std::move(mock_permission_manager)); + // Create a StoragePartition with the correct BrowserContext so that the // BackgroundSyncManager can find the BrowserContext through it. storage_partition_impl_.reset(new StoragePartitionImpl( helper_->browser_context(), base::FilePath(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr)); + nullptr, nullptr, nullptr)); helper_->context_wrapper()->set_storage_partition( storage_partition_impl_.get()); - power_monitor_source_ = new TestPowerSource(); - // power_monitor_ takes ownership of power_monitor_source. - power_monitor_.reset(new base::PowerMonitor( - scoped_ptr(power_monitor_source_))); - - SetOnBatteryPower(false); - - scoped_ptr background_sync_controller( - new TestBackgroundSyncController()); - test_controller_ = background_sync_controller.get(); - helper_->browser_context()->SetBackgroundSyncController( - std::move(background_sync_controller)); - SetMaxSyncAttemptsAndRestartManager(1); // Wait for storage to finish initializing before registering service @@ -398,28 +195,22 @@ class BackgroundSyncManagerTest : public testing::Test { } } - void SetOnBatteryPower(bool on_battery_power) { - power_monitor_source_->GeneratePowerStateEvent(on_battery_power); - base::RunLoop().RunUntilIdle(); - } - void StatusAndRegistrationCallback( bool* was_called, BackgroundSyncStatus status, - scoped_ptr registration_handle) { + scoped_ptr registration) { *was_called = true; callback_status_ = status; - callback_registration_handle_ = std::move(registration_handle); + callback_registration_ = std::move(registration); } void StatusAndRegistrationsCallback( bool* was_called, BackgroundSyncStatus status, - scoped_ptr> - registration_handles) { + scoped_ptr> registrations) { *was_called = true; callback_status_ = status; - callback_registration_handles_ = std::move(registration_handles); + callback_registrations_ = std::move(registrations); } void StatusCallback(bool* was_called, BackgroundSyncStatus status) { @@ -429,8 +220,6 @@ class BackgroundSyncManagerTest : public testing::Test { protected: void CreateBackgroundSyncManager() { - ClearRegistrationHandles(); - test_background_sync_manager_ = new TestBackgroundSyncManager(helper_->context_wrapper()); background_sync_manager_.reset(test_background_sync_manager_); @@ -451,12 +240,6 @@ class BackgroundSyncManagerTest : public testing::Test { base::RunLoop().RunUntilIdle(); } - // Clear the registrations so that the BackgroundSyncManager can release them. - void ClearRegistrationHandles() { - callback_registration_handle_.reset(); - callback_registration_handles_.reset(); - } - void SetupBackgroundSyncManager() { CreateBackgroundSyncManager(); InitBackgroundSyncManager(); @@ -475,7 +258,6 @@ class BackgroundSyncManagerTest : public testing::Test { } void DeleteBackgroundSyncManager() { - ClearRegistrationHandles(); background_sync_manager_.reset(); test_background_sync_manager_ = nullptr; test_clock_ = nullptr; @@ -490,7 +272,7 @@ class BackgroundSyncManagerTest : public testing::Test { const BackgroundSyncRegistrationOptions& options) { bool was_called = false; background_sync_manager_->Register( - sw_registration_id, options, true /* requested_from_service_worker */, + sw_registration_id, options, base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, base::Unretained(this), &was_called)); base::RunLoop().RunUntilIdle(); @@ -498,58 +280,9 @@ class BackgroundSyncManagerTest : public testing::Test { return callback_status_ == BACKGROUND_SYNC_STATUS_OK; } - bool RegisterFromDocumentWithServiceWorkerId( - int64_t sw_registration_id, - const BackgroundSyncRegistrationOptions& options) { - bool was_called = false; - background_sync_manager_->Register( - sw_registration_id, options, false /* requested_from_service_worker */, - base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, - base::Unretained(this), &was_called)); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(was_called); - return callback_status_ == BACKGROUND_SYNC_STATUS_OK; - } - - bool Unregister(BackgroundSyncRegistrationHandle* registration_handle) { - return UnregisterWithServiceWorkerId(sw_registration_id_1_, - registration_handle); - } - - bool UnregisterWithServiceWorkerId( - int64_t sw_registration_id, - BackgroundSyncRegistrationHandle* registration_handle) { - bool was_called = false; - registration_handle->Unregister( - sw_registration_id, - base::Bind(&BackgroundSyncManagerTest::StatusCallback, - base::Unretained(this), &was_called)); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(was_called); - return callback_status_ == BACKGROUND_SYNC_STATUS_OK; - } - - bool NotifyWhenFinished( - BackgroundSyncRegistrationHandle* registration_handle) { - callback_finished_called_ = false; - callback_finished_status_ = BACKGROUND_SYNC_STATUS_NOT_FOUND; - callback_finished_state_ = BACKGROUND_SYNC_STATE_FAILED; - - registration_handle->NotifyWhenFinished( - base::Bind(&NotifyWhenFinishedCallback, &callback_finished_called_, - &callback_finished_status_, &callback_finished_state_)); - base::RunLoop().RunUntilIdle(); - - if (callback_finished_called_) - EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, callback_finished_status_); - - return callback_finished_called_; - } - - BackgroundSyncState FinishedState() { - EXPECT_TRUE(callback_finished_called_); - EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, callback_finished_status_); - return callback_finished_state_; + MockPermissionManager* GetPermissionManager() { + return static_cast( + helper_->browser_context()->GetPermissionManager()); } bool GetRegistration( @@ -562,32 +295,36 @@ class BackgroundSyncManagerTest : public testing::Test { int64_t sw_registration_id, const BackgroundSyncRegistrationOptions& registration_options) { bool was_called = false; - background_sync_manager_->GetRegistration( - sw_registration_id, registration_options.tag, - registration_options.periodicity, - base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, + background_sync_manager_->GetRegistrations( + sw_registration_id, + base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationsCallback, base::Unretained(this), &was_called)); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(was_called); if (callback_status_ == BACKGROUND_SYNC_STATUS_OK) { - EXPECT_STREQ(registration_options.tag.c_str(), - callback_registration_handle_->options()->tag.c_str()); + for (auto iter = callback_registrations_->begin(); + iter < callback_registrations_->end(); ++iter) { + if ((*iter)->options()->tag == registration_options.tag) { + // Transfer the matching registration out of the vector into + // callback_registration_ for testing. + callback_registration_.reset(*iter); + callback_registrations_->weak_erase(iter); + return true; + } + } } - - return callback_status_ == BACKGROUND_SYNC_STATUS_OK; + return false; } - bool GetRegistrations(SyncPeriodicity periodicity) { - return GetRegistrationWithServiceWorkerId(sw_registration_id_1_, - periodicity); + bool GetRegistrations() { + return GetRegistrationsWithServiceWorkerId(sw_registration_id_1_); } - bool GetRegistrationWithServiceWorkerId(int64_t sw_registration_id, - SyncPeriodicity periodicity) { + bool GetRegistrationsWithServiceWorkerId(int64_t sw_registration_id) { bool was_called = false; background_sync_manager_->GetRegistrations( - sw_registration_id, periodicity, + sw_registration_id, base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationsCallback, base::Unretained(this), &was_called)); base::RunLoop().RunUntilIdle(); @@ -596,6 +333,11 @@ class BackgroundSyncManagerTest : public testing::Test { return callback_status_ == BACKGROUND_SYNC_STATUS_OK; } + MockBackgroundSyncController* GetController() { + return static_cast( + helper_->browser_context()->GetBackgroundSyncController()); + } + void StorageRegistrationCallback(ServiceWorkerStatusCode result) { callback_sw_status_code_ = result; } @@ -616,23 +358,24 @@ class BackgroundSyncManagerTest : public testing::Test { } void SetupForSyncEvent( - const TestBackgroundSyncManager::OneShotCallback& callback) { - test_background_sync_manager_->set_one_shot_callback(callback); + const TestBackgroundSyncManager::DispatchSyncCallback& callback) { + test_background_sync_manager_->set_dispatch_sync_callback(callback); SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); } void InitSyncEventTest() { SetupForSyncEvent( - base::Bind(OneShotSuccessfulCallback, &sync_events_called_)); + base::Bind(DispatchSyncSuccessfulCallback, &sync_events_called_)); } void InitFailedSyncEventTest() { - SetupForSyncEvent(base::Bind(OneShotFailedCallback, &sync_events_called_)); + SetupForSyncEvent( + base::Bind(DispatchSyncFailedCallback, &sync_events_called_)); } void InitDelayedSyncEventTest() { - SetupForSyncEvent(base::Bind(OneShotDelayedCallback, &sync_events_called_, - &sync_fired_callback_)); + SetupForSyncEvent(base::Bind(DispatchSyncDelayedCallback, + &sync_events_called_, &sync_fired_callback_)); } void RegisterAndVerifySyncEventDelayed( @@ -656,7 +399,7 @@ class BackgroundSyncManagerTest : public testing::Test { void SetMaxSyncAttemptsAndRestartManager(int max_sync_attempts) { BackgroundSyncParameters* parameters = - test_controller_->background_sync_parameters(); + GetController()->background_sync_parameters(); parameters->max_sync_attempts = max_sync_attempts; // Restart the BackgroundSyncManager so that it updates its parameters. @@ -665,13 +408,10 @@ class BackgroundSyncManagerTest : public testing::Test { TestBrowserThreadBundle browser_thread_bundle_; scoped_ptr network_change_notifier_; - TestPowerSource* power_monitor_source_ = nullptr; // owned by power_monitor_ - scoped_ptr power_monitor_; scoped_ptr helper_; scoped_ptr background_sync_manager_; scoped_ptr storage_partition_impl_; TestBackgroundSyncManager* test_background_sync_manager_ = nullptr; - TestBackgroundSyncController* test_controller_; base::SimpleTestClock* test_clock_ = nullptr; int64_t sw_registration_id_1_; @@ -684,14 +424,9 @@ class BackgroundSyncManagerTest : public testing::Test { // Callback values. BackgroundSyncStatus callback_status_ = BACKGROUND_SYNC_STATUS_OK; - scoped_ptr callback_registration_handle_; - scoped_ptr> - callback_registration_handles_; + scoped_ptr callback_registration_; + scoped_ptr> callback_registrations_; ServiceWorkerStatusCode callback_sw_status_code_ = SERVICE_WORKER_OK; - bool callback_finished_called_ = false; - BackgroundSyncStatus callback_finished_status_ = - BACKGROUND_SYNC_STATUS_NOT_FOUND; - BackgroundSyncState callback_finished_state_ = BACKGROUND_SYNC_STATE_FAILED; int sync_events_called_ = 0; ServiceWorkerVersion::StatusCallback sync_fired_callback_; }; @@ -700,11 +435,11 @@ TEST_F(BackgroundSyncManagerTest, Register) { EXPECT_TRUE(Register(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, RegistractionIntact) { +TEST_F(BackgroundSyncManagerTest, RegistrationIntact) { EXPECT_TRUE(Register(sync_options_1_)); EXPECT_STREQ(sync_options_1_.tag.c_str(), - callback_registration_handle_->options()->tag.c_str()); - EXPECT_TRUE(callback_registration_handle_->IsValid()); + callback_registration_->options()->tag.c_str()); + EXPECT_TRUE(callback_registration_->IsValid()); } TEST_F(BackgroundSyncManagerTest, RegisterWithoutLiveSWRegistration) { @@ -719,36 +454,6 @@ TEST_F(BackgroundSyncManagerTest, RegisterWithoutActiveSWRegistration) { EXPECT_EQ(BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, callback_status_); } -TEST_F(BackgroundSyncManagerTest, RegisterOverwrites) { - EXPECT_TRUE(Register(sync_options_1_)); - scoped_ptr first_registration_handle = - std::move(callback_registration_handle_); - - sync_options_1_.min_period = 100; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_LT(first_registration_handle->handle_id(), - callback_registration_handle_->handle_id()); - EXPECT_FALSE(first_registration_handle->options()->Equals( - *callback_registration_handle_->options())); -} - -TEST_F(BackgroundSyncManagerTest, RegisterOverlappingPeriodicAndOneShotTags) { - // Registrations with the same tags but different periodicities should not - // collide. - sync_options_1_.tag = ""; - sync_options_2_.tag = ""; - sync_options_1_.periodicity = SYNC_PERIODIC; - sync_options_2_.periodicity = SYNC_ONE_SHOT; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_EQ(SYNC_PERIODIC, - callback_registration_handle_->options()->periodicity); - EXPECT_TRUE(GetRegistration(sync_options_2_)); - EXPECT_EQ(SYNC_ONE_SHOT, - callback_registration_handle_->options()->periodicity); -} - TEST_F(BackgroundSyncManagerTest, RegisterBadBackend) { test_background_sync_manager_->set_corrupt_backend(true); EXPECT_FALSE(Register(sync_options_1_)); @@ -757,18 +462,26 @@ TEST_F(BackgroundSyncManagerTest, RegisterBadBackend) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, DuplicateRegistrationHandle) { - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE( - sync_options_1_.Equals(*callback_registration_handle_->options())); +TEST_F(BackgroundSyncManagerTest, RegisterPermissionDenied) { + GURL expected_origin = GURL(kPattern1).GetOrigin(); + MockPermissionManager* mock_permission_manager = GetPermissionManager(); - scoped_ptr dup_handle = - background_sync_manager_->DuplicateRegistrationHandle( - callback_registration_handle_->handle_id()); + EXPECT_CALL(*mock_permission_manager, + GetPermissionStatus(PermissionType::BACKGROUND_SYNC, + expected_origin, expected_origin)) + .WillOnce(testing::Return(blink::mojom::PermissionStatus::DENIED)); + EXPECT_FALSE(Register(sync_options_1_)); +} + +TEST_F(BackgroundSyncManagerTest, RegisterPermissionGranted) { + GURL expected_origin = GURL(kPattern1).GetOrigin(); + MockPermissionManager* mock_permission_manager = GetPermissionManager(); - EXPECT_TRUE(sync_options_1_.Equals(*dup_handle->options())); - EXPECT_NE(callback_registration_handle_->handle_id(), - dup_handle->handle_id()); + EXPECT_CALL(*mock_permission_manager, + GetPermissionStatus(PermissionType::BACKGROUND_SYNC, + expected_origin, expected_origin)) + .WillOnce(testing::Return(blink::mojom::PermissionStatus::GRANTED)); + EXPECT_TRUE(Register(sync_options_1_)); } TEST_F(BackgroundSyncManagerTest, TwoRegistrations) { @@ -799,115 +512,70 @@ TEST_F(BackgroundSyncManagerTest, GetRegistrationBadBackend) { } TEST_F(BackgroundSyncManagerTest, GetRegistrationsZero) { - EXPECT_TRUE(GetRegistrations(SYNC_ONE_SHOT)); - EXPECT_EQ(0u, callback_registration_handles_->size()); + EXPECT_TRUE(GetRegistrations()); + EXPECT_EQ(0u, callback_registrations_->size()); } TEST_F(BackgroundSyncManagerTest, GetRegistrationsOne) { EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(GetRegistrations(sync_options_1_.periodicity)); + EXPECT_TRUE(GetRegistrations()); - EXPECT_EQ(1u, callback_registration_handles_->size()); - sync_options_1_.Equals(*(*callback_registration_handles_)[0]->options()); + EXPECT_EQ(1u, callback_registrations_->size()); + sync_options_1_.Equals(*(*callback_registrations_)[0]->options()); } TEST_F(BackgroundSyncManagerTest, GetRegistrationsTwo) { - EXPECT_EQ(sync_options_1_.periodicity, sync_options_2_.periodicity); - - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); - EXPECT_TRUE(GetRegistrations(sync_options_1_.periodicity)); - - EXPECT_EQ(2u, callback_registration_handles_->size()); - sync_options_1_.Equals(*(*callback_registration_handles_)[0]->options()); - sync_options_2_.Equals(*(*callback_registration_handles_)[1]->options()); -} - -TEST_F(BackgroundSyncManagerTest, GetRegistrationsPeriodicity) { - sync_options_1_.periodicity = SYNC_ONE_SHOT; - sync_options_2_.periodicity = SYNC_PERIODIC; EXPECT_TRUE(Register(sync_options_1_)); EXPECT_TRUE(Register(sync_options_2_)); + EXPECT_TRUE(GetRegistrations()); - EXPECT_TRUE(GetRegistrations(SYNC_ONE_SHOT)); - EXPECT_EQ(1u, callback_registration_handles_->size()); - sync_options_1_.Equals(*(*callback_registration_handles_)[0]->options()); - - EXPECT_TRUE(GetRegistrations(SYNC_PERIODIC)); - EXPECT_EQ(1u, callback_registration_handles_->size()); - sync_options_2_.Equals(*(*callback_registration_handles_)[0]->options()); + EXPECT_EQ(2u, callback_registrations_->size()); + sync_options_1_.Equals(*(*callback_registrations_)[0]->options()); + sync_options_2_.Equals(*(*callback_registrations_)[1]->options()); } TEST_F(BackgroundSyncManagerTest, GetRegistrationsBadBackend) { EXPECT_TRUE(Register(sync_options_1_)); test_background_sync_manager_->set_corrupt_backend(true); - EXPECT_TRUE(GetRegistrations(sync_options_1_.periodicity)); + EXPECT_TRUE(GetRegistrations()); EXPECT_FALSE(Register(sync_options_2_)); // Registration should have discovered the bad backend and disabled the // BackgroundSyncManager. - EXPECT_FALSE(GetRegistrations(sync_options_1_.periodicity)); + EXPECT_FALSE(GetRegistrations()); test_background_sync_manager_->set_corrupt_backend(false); - EXPECT_FALSE(GetRegistrations(sync_options_1_.periodicity)); + EXPECT_FALSE(GetRegistrations()); } -TEST_F(BackgroundSyncManagerTest, Unregister) { - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); -} TEST_F(BackgroundSyncManagerTest, Reregister) { EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_TRUE(Register(sync_options_1_)); -} - -TEST_F(BackgroundSyncManagerTest, UnregisterSecond) { EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); } -TEST_F(BackgroundSyncManagerTest, UnregisterBadBackend) { - sync_options_1_.min_period += 1; +TEST_F(BackgroundSyncManagerTest, ReregisterSecond) { EXPECT_TRUE(Register(sync_options_1_)); EXPECT_TRUE(Register(sync_options_2_)); - test_background_sync_manager_->set_corrupt_backend(true); - EXPECT_FALSE(Unregister(callback_registration_handle_.get())); - // Unregister should have discovered the bad backend and disabled the - // BackgroundSyncManager. - test_background_sync_manager_->set_corrupt_backend(false); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_FALSE(GetRegistration(sync_options_2_)); + EXPECT_TRUE(Register(sync_options_2_)); } TEST_F(BackgroundSyncManagerTest, RegisterMaxTagLength) { sync_options_1_.tag = std::string(MaxTagLength(), 'a'); EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - sync_options_1_.tag = std::string(MaxTagLength() + 1, 'a'); - EXPECT_FALSE(Register(sync_options_1_)); + sync_options_2_.tag = std::string(MaxTagLength() + 1, 'b'); + EXPECT_FALSE(Register(sync_options_2_)); EXPECT_EQ(BACKGROUND_SYNC_STATUS_NOT_ALLOWED, callback_status_); } TEST_F(BackgroundSyncManagerTest, RegistrationIncreasesId) { EXPECT_TRUE(Register(sync_options_1_)); - scoped_ptr registered_handle = - std::move(callback_registration_handle_); BackgroundSyncRegistration::RegistrationId cur_id = - registered_handle->handle_id(); + callback_registration_->id(); EXPECT_TRUE(GetRegistration(sync_options_1_)); EXPECT_TRUE(Register(sync_options_2_)); - EXPECT_LT(cur_id, callback_registration_handle_->handle_id()); - cur_id = callback_registration_handle_->handle_id(); - - EXPECT_TRUE(Unregister(registered_handle.get())); - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_LT(cur_id, callback_registration_handle_->handle_id()); + EXPECT_LT(cur_id, callback_registration_->id()); } TEST_F(BackgroundSyncManagerTest, RebootRecovery) { @@ -960,34 +628,33 @@ TEST_F(BackgroundSyncManagerTest, SequentialOperations) { SetupDelayedBackgroundSyncManager(); bool register_called = false; - bool get_registration_called = false; + bool get_registrations_called = false; test_background_sync_manager_->Register( sw_registration_id_1_, sync_options_1_, - true /* requested_from_service_worker */, base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, base::Unretained(this), ®ister_called)); - test_background_sync_manager_->GetRegistration( - sw_registration_id_1_, sync_options_1_.tag, sync_options_1_.periodicity, - base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, - base::Unretained(this), &get_registration_called)); + test_background_sync_manager_->GetRegistrations( + sw_registration_id_1_, + base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationsCallback, + base::Unretained(this), &get_registrations_called)); base::RunLoop().RunUntilIdle(); // Init should be blocked while loading from the backend. EXPECT_FALSE(register_called); - EXPECT_FALSE(get_registration_called); + EXPECT_FALSE(get_registrations_called); - test_background_sync_manager_->Continue(); + test_background_sync_manager_->ResumeBackendOperation(); base::RunLoop().RunUntilIdle(); // Register should be blocked while storing to the backend. EXPECT_FALSE(register_called); - EXPECT_FALSE(get_registration_called); + EXPECT_FALSE(get_registrations_called); - test_background_sync_manager_->Continue(); + test_background_sync_manager_->ResumeBackendOperation(); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(register_called); EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, callback_status_); - // GetRegistration should run immediately as it doesn't write to disk. - EXPECT_TRUE(get_registration_called); + // GetRegistrations should run immediately as it doesn't write to disk. + EXPECT_TRUE(get_registrations_called); } TEST_F(BackgroundSyncManagerTest, UnregisterServiceWorker) { @@ -1004,7 +671,6 @@ TEST_F(BackgroundSyncManagerTest, bool callback_called = false; test_background_sync_manager_->Register( sw_registration_id_1_, sync_options_2_, - true /* requested_from_service_worker */, base::Bind(&BackgroundSyncManagerTest::StatusAndRegistrationCallback, base::Unretained(this), &callback_called)); @@ -1012,7 +678,7 @@ TEST_F(BackgroundSyncManagerTest, EXPECT_FALSE(callback_called); UnregisterServiceWorker(sw_registration_id_1_); - test_background_sync_manager_->Continue(); + test_background_sync_manager_->ResumeBackendOperation(); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(callback_called); EXPECT_EQ(BACKGROUND_SYNC_STATUS_STORAGE_ERROR, callback_status_); @@ -1078,23 +744,6 @@ TEST_F(BackgroundSyncManagerTest, RegistrationEqualsTag) { EXPECT_FALSE(reg_1.Equals(reg_2)); } -TEST_F(BackgroundSyncManagerTest, RegistrationEqualsPeriodicity) { - BackgroundSyncRegistration reg_1; - BackgroundSyncRegistration reg_2; - EXPECT_TRUE(reg_1.Equals(reg_2)); - reg_1.options()->periodicity = SYNC_PERIODIC; - reg_2.options()->periodicity = SYNC_ONE_SHOT; - EXPECT_FALSE(reg_1.Equals(reg_2)); -} - -TEST_F(BackgroundSyncManagerTest, RegistrationEqualsMinPeriod) { - BackgroundSyncRegistration reg_1; - BackgroundSyncRegistration reg_2; - EXPECT_TRUE(reg_1.Equals(reg_2)); - reg_2.options()->min_period = reg_1.options()->min_period + 1; - EXPECT_FALSE(reg_1.Equals(reg_2)); -} - TEST_F(BackgroundSyncManagerTest, RegistrationEqualsNetworkState) { BackgroundSyncRegistration reg_1; BackgroundSyncRegistration reg_2; @@ -1104,26 +753,14 @@ TEST_F(BackgroundSyncManagerTest, RegistrationEqualsNetworkState) { EXPECT_FALSE(reg_1.Equals(reg_2)); } -TEST_F(BackgroundSyncManagerTest, RegistrationEqualsPowerState) { - BackgroundSyncRegistration reg_1; - BackgroundSyncRegistration reg_2; - EXPECT_TRUE(reg_1.Equals(reg_2)); - reg_1.options()->power_state = POWER_STATE_AUTO; - reg_2.options()->power_state = POWER_STATE_AVOID_DRAINING; - EXPECT_FALSE(reg_1.Equals(reg_2)); -} - TEST_F(BackgroundSyncManagerTest, StoreAndRetrievePreservesValues) { + InitDelayedSyncEventTest(); BackgroundSyncRegistrationOptions options; + // Set non-default values for each field. options.tag = "foo"; - EXPECT_NE(SYNC_PERIODIC, options.periodicity); - options.periodicity = SYNC_PERIODIC; - options.min_period += 1; - EXPECT_NE(NETWORK_STATE_ANY, options.network_state); - options.network_state = NETWORK_STATE_ANY; - EXPECT_NE(POWER_STATE_AUTO, options.power_state); - options.power_state = POWER_STATE_AUTO; + EXPECT_NE(NETWORK_STATE_AVOID_CELLULAR, options.network_state); + options.network_state = NETWORK_STATE_AVOID_CELLULAR; // Store the registration. EXPECT_TRUE(Register(options)); @@ -1133,49 +770,17 @@ TEST_F(BackgroundSyncManagerTest, StoreAndRetrievePreservesValues) { SetupBackgroundSyncManager(); EXPECT_TRUE(GetRegistration(options)); - EXPECT_TRUE(options.Equals(*callback_registration_handle_->options())); + EXPECT_TRUE(options.Equals(*callback_registration_->options())); } TEST_F(BackgroundSyncManagerTest, EmptyTagSupported) { - sync_options_1_.tag = "a"; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_TRUE( - sync_options_1_.Equals(*callback_registration_handle_->options())); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); -} - -TEST_F(BackgroundSyncManagerTest, OverlappingPeriodicAndOneShotTags) { - // Registrations with the same tags but different periodicities should not - // collide. sync_options_1_.tag = ""; - sync_options_2_.tag = ""; - sync_options_1_.periodicity = SYNC_PERIODIC; - sync_options_2_.periodicity = SYNC_ONE_SHOT; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); - - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_EQ(SYNC_PERIODIC, - callback_registration_handle_->options()->periodicity); - EXPECT_TRUE(GetRegistration(sync_options_2_)); - EXPECT_EQ(SYNC_ONE_SHOT, - callback_registration_handle_->options()->periodicity); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_TRUE(GetRegistration(sync_options_2_)); - EXPECT_EQ(SYNC_ONE_SHOT, - callback_registration_handle_->options()->periodicity); - - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_2_)); + EXPECT_TRUE(sync_options_1_.Equals(*callback_registration_->options())); } -TEST_F(BackgroundSyncManagerTest, OneShotFiresOnRegistration) { +TEST_F(BackgroundSyncManagerTest, FiresOnRegistration) { InitSyncEventTest(); EXPECT_TRUE(Register(sync_options_1_)); @@ -1183,139 +788,9 @@ TEST_F(BackgroundSyncManagerTest, OneShotFiresOnRegistration) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedAfterEventSuccess) { - InitSyncEventTest(); - - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_EQ(1, sync_events_called_); - - EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeEventSuccess) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_OK); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1, sync_events_called_); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyWhenFinishedBeforeUnregisteredEventSuccess) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Unregistering should set the state to UNREGISTERED but finished shouldn't - // be called until the event finishes firing, at which point its state should - // be SUCCESS. - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_OK); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyWhenFinishedBeforeUnregisteredEventFailure) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Unregistering should set the state to UNREGISTERED but finished shouldn't - // be called until the event finishes firing, at which point its state should - // be FAILED. - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1, sync_events_called_); - EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyWhenFinishedBeforeUnregisteredEventFires) { - InitSyncEventTest(); - - SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyWhenFinishedBeforeEventSuccessDroppedHandle) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Drop the client's handle to the registration before the event fires, ensure - // that the finished callback is still run. - callback_registration_handle_ = nullptr; - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_OK); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1, sync_events_called_); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedAfterEventFailure) { - InitFailedSyncEventTest(); - - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_EQ(1, sync_events_called_); - - EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeEventFailure) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedAfterUnregistered) { - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - - EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeUnregistered) { - Register(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); -} - TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptFails) { InitDelayedSyncEventTest(); RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); // Reregister the event mid-sync EXPECT_TRUE(Register(sync_options_1_)); @@ -1323,19 +798,16 @@ TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptFails) { // The first sync attempt fails. sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(callback_finished_called_); // It should fire again since it was reregistered mid-sync. EXPECT_TRUE(GetRegistration(sync_options_1_)); sync_fired_callback_.Run(SERVICE_WORKER_OK); EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); } TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptSucceeds) { InitDelayedSyncEventTest(); RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); // Reregister the event mid-sync EXPECT_TRUE(Register(sync_options_1_)); @@ -1343,198 +815,67 @@ TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptSucceeds) { // The first sync event succeeds. sync_fired_callback_.Run(SERVICE_WORKER_OK); base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(callback_finished_called_); // It should fire again since it was reregistered mid-sync. EXPECT_TRUE(GetRegistration(sync_options_1_)); sync_fired_callback_.Run(SERVICE_WORKER_OK); EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyUnregisteredMidSyncNoRetryAttemptsLeft) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Unregister the event mid-sync. - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); - base::RunLoop().RunUntilIdle(); - - // Since there were no retry attempts left, the sync ultimately failed. - EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, - NotifyUnregisteredMidSyncWithRetryAttemptsLeft) { - SetMaxSyncAttemptsAndRestartManager(2); - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); - - // Unregister the event mid-sync. - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - - // Finish firing the event. - sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); - base::RunLoop().RunUntilIdle(); - // Since there was one retry attempt left, the sync didn't completely fail - // before it was unregistered. - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); } TEST_F(BackgroundSyncManagerTest, OverwritePendingRegistration) { - // An overwritten pending registration should complete with - // BACKGROUND_SYNC_STATE_UNREGISTERED. - sync_options_1_.power_state = POWER_STATE_AVOID_DRAINING; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_EQ(POWER_STATE_AVOID_DRAINING, - callback_registration_handle_->options()->power_state); - scoped_ptr original_handle = - std::move(callback_registration_handle_); + InitFailedSyncEventTest(); - // Overwrite the pending registration. - sync_options_1_.power_state = POWER_STATE_AUTO; + // Prevent the first sync from running so that it stays in a pending state. + SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); EXPECT_TRUE(Register(sync_options_1_)); EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_EQ(POWER_STATE_AUTO, - callback_registration_handle_->options()->power_state); - EXPECT_TRUE(NotifyWhenFinished(original_handle.get())); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); - EXPECT_EQ(0, sync_events_called_); -} - -TEST_F(BackgroundSyncManagerTest, OverwriteFiringRegistrationWhichSucceeds) { - // An overwritten pending registration should complete with - // BACKGROUND_SYNC_STATE_SUCCESS if firing completes successfully. - InitDelayedSyncEventTest(); - - sync_options_1_.power_state = POWER_STATE_AVOID_DRAINING; - RegisterAndVerifySyncEventDelayed(sync_options_1_); - scoped_ptr original_handle = - std::move(callback_registration_handle_); - - // The next registration won't block. - InitSyncEventTest(); - - // Overwrite the firing registration. - sync_options_1_.power_state = POWER_STATE_AUTO; + // Overwrite the first sync. It should still be pending. EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_FALSE(NotifyWhenFinished(original_handle.get())); + EXPECT_TRUE(GetRegistration(sync_options_1_)); - // Successfully finish the first event. - sync_fired_callback_.Run(SERVICE_WORKER_OK); + // Verify that it only gets to run once. + SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); + EXPECT_EQ(1, sync_events_called_); + EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, OverwriteFiringRegistrationWhichFails) { - // An overwritten pending registration should complete with - // BACKGROUND_SYNC_STATE_FAILED if firing fails. +TEST_F(BackgroundSyncManagerTest, DisableWhilePending) { InitDelayedSyncEventTest(); - - sync_options_1_.power_state = POWER_STATE_AVOID_DRAINING; - RegisterAndVerifySyncEventDelayed(sync_options_1_); - scoped_ptr original_handle = - std::move(callback_registration_handle_); - - // The next registration won't block. - InitSyncEventTest(); - - // Overwrite the firing registration. - sync_options_1_.power_state = POWER_STATE_AUTO; - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_FALSE(NotifyWhenFinished(original_handle.get())); - - // Fail the first event. - sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState()); -} - -TEST_F(BackgroundSyncManagerTest, DisableWhilePendingNotifiesFinished) { - InitSyncEventTest(); - - // Register a one-shot that must wait for network connectivity before it - // can fire. SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); // Corrupting the backend should result in the manager disabling itself on the - // next operation. While disabling, it should finalize any pending - // registrations. + // next operation. test_background_sync_manager_->set_corrupt_backend(true); EXPECT_FALSE(Register(sync_options_2_)); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState()); + + test_background_sync_manager_->set_corrupt_backend(false); + SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, sync_events_called_); } -TEST_F(BackgroundSyncManagerTest, DisableWhileFiringNotifiesFinished) { +TEST_F(BackgroundSyncManagerTest, DisableWhileFiring) { InitDelayedSyncEventTest(); // Register a one-shot that pauses mid-fire. RegisterAndVerifySyncEventDelayed(sync_options_1_); - EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get())); // Corrupting the backend should result in the manager disabling itself on the - // next operation. Even though the manager is disabled, the firing sync event - // should still be able to complete successfully and notify as much. + // next operation. test_background_sync_manager_->set_corrupt_backend(true); EXPECT_FALSE(Register(sync_options_2_)); - EXPECT_FALSE(callback_finished_called_); test_background_sync_manager_->set_corrupt_backend(false); - // Successfully complete the firing event. + // Successfully complete the firing event. We can't verify that it actually + // completed but at least we can test that it doesn't crash. sync_fired_callback_.Run(SERVICE_WORKER_OK); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState()); -} - -// TODO(jkarlin): Change this to a periodic test as one-shots can't be power -// dependent according to spec. -TEST_F(BackgroundSyncManagerTest, OneShotFiresOnPowerChange) { - InitSyncEventTest(); - sync_options_1_.power_state = POWER_STATE_AVOID_DRAINING; - - SetOnBatteryPower(true); - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_EQ(0, sync_events_called_); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - - SetOnBatteryPower(false); - EXPECT_EQ(1, sync_events_called_); - EXPECT_FALSE(GetRegistration(sync_options_1_)); } -// TODO(jkarlin): Change this to a periodic test as one-shots can't be power -// dependent according to spec. -TEST_F(BackgroundSyncManagerTest, MultipleOneShotsFireOnPowerChange) { - InitSyncEventTest(); - sync_options_1_.power_state = POWER_STATE_AVOID_DRAINING; - sync_options_2_.power_state = POWER_STATE_AVOID_DRAINING; - - SetOnBatteryPower(true); - EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_TRUE(Register(sync_options_2_)); - EXPECT_EQ(0, sync_events_called_); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - EXPECT_TRUE(GetRegistration(sync_options_2_)); - - SetOnBatteryPower(false); - EXPECT_EQ(2, sync_events_called_); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_FALSE(GetRegistration(sync_options_2_)); -} - -TEST_F(BackgroundSyncManagerTest, OneShotFiresOnNetworkChange) { +TEST_F(BackgroundSyncManagerTest, FiresOnNetworkChange) { InitSyncEventTest(); SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); @@ -1548,7 +889,7 @@ TEST_F(BackgroundSyncManagerTest, OneShotFiresOnNetworkChange) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, MultipleOneShotsFireOnNetworkChange) { +TEST_F(BackgroundSyncManagerTest, MultipleRegistrationsFireOnNetworkChange) { InitSyncEventTest(); SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); @@ -1565,7 +906,7 @@ TEST_F(BackgroundSyncManagerTest, MultipleOneShotsFireOnNetworkChange) { EXPECT_FALSE(GetRegistration(sync_options_2_)); } -TEST_F(BackgroundSyncManagerTest, OneShotFiresOnManagerRestart) { +TEST_F(BackgroundSyncManagerTest, FiresOnManagerRestart) { InitSyncEventTest(); // Initially the event won't run because there is no network. @@ -1587,7 +928,7 @@ TEST_F(BackgroundSyncManagerTest, OneShotFiresOnManagerRestart) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, FailedOneShotShouldBeRemoved) { +TEST_F(BackgroundSyncManagerTest, FailedRegistrationShouldBeRemoved) { InitFailedSyncEventTest(); EXPECT_TRUE(Register(sync_options_1_)); @@ -1595,7 +936,7 @@ TEST_F(BackgroundSyncManagerTest, FailedOneShotShouldBeRemoved) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, FailedOneShotReregisteredAndFires) { +TEST_F(BackgroundSyncManagerTest, FailedRegistrationReregisteredAndFires) { InitFailedSyncEventTest(); // The initial sync event fails. @@ -1612,7 +953,7 @@ TEST_F(BackgroundSyncManagerTest, FailedOneShotReregisteredAndFires) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, DelayOneShotMidSync) { +TEST_F(BackgroundSyncManagerTest, DelayMidSync) { InitDelayedSyncEventTest(); RegisterAndVerifySyncEventDelayed(sync_options_1_); @@ -1624,50 +965,6 @@ TEST_F(BackgroundSyncManagerTest, DelayOneShotMidSync) { EXPECT_FALSE(GetRegistration(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, OverwriteRegistrationMidSync) { - InitDelayedSyncEventTest(); - - sync_options_1_.network_state = NETWORK_STATE_ANY; - SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - - // Don't delay the next sync. - test_background_sync_manager_->set_one_shot_callback( - base::Bind(OneShotSuccessfulCallback, &sync_events_called_)); - - // Register a different sync event with the same tag, overwriting the first. - sync_options_1_.network_state = NETWORK_STATE_ONLINE; - EXPECT_TRUE(Register(sync_options_1_)); - - // The new sync event won't run as the network requirements aren't met. - EXPECT_EQ(1, sync_events_called_); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - - // Finish the first event, note that the second is still registered. - sync_fired_callback_.Run(SERVICE_WORKER_OK); - EXPECT_EQ(1, sync_events_called_); - EXPECT_TRUE(GetRegistration(sync_options_1_)); - - // Change the network and the second should run. - SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(2, sync_events_called_); - EXPECT_FALSE(GetRegistration(sync_options_1_)); -} - -TEST_F(BackgroundSyncManagerTest, UnregisterOneShotMidSync) { - InitDelayedSyncEventTest(); - - RegisterAndVerifySyncEventDelayed(sync_options_1_); - - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); - - sync_fired_callback_.Run(SERVICE_WORKER_OK); - EXPECT_FALSE(GetRegistration(sync_options_1_)); -} - TEST_F(BackgroundSyncManagerTest, BadBackendMidSync) { InitDelayedSyncEventTest(); @@ -1712,34 +1009,19 @@ TEST_F(BackgroundSyncManagerTest, KillManagerMidSync) { EXPECT_EQ(2, sync_events_called_); } -TEST_F(BackgroundSyncManagerTest, RegisterFromServiceWorkerWithoutMainFrame) { +TEST_F(BackgroundSyncManagerTest, RegisterWithoutMainFrame) { test_background_sync_manager_->set_has_main_frame_provider_host(false); EXPECT_FALSE(Register(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, - RegisterFromDocumentWithoutMainFrameProviderHost) { - test_background_sync_manager_->set_has_main_frame_provider_host(false); - EXPECT_TRUE(RegisterFromDocumentWithServiceWorkerId(sw_registration_id_1_, - sync_options_1_)); -} - -TEST_F(BackgroundSyncManagerTest, - RegisterExistingFromServiceWorkerWithoutMainFrame) { +TEST_F(BackgroundSyncManagerTest, RegisterExistingWithoutMainFrame) { EXPECT_TRUE(Register(sync_options_1_)); test_background_sync_manager_->set_has_main_frame_provider_host(false); EXPECT_FALSE(Register(sync_options_1_)); } -TEST_F(BackgroundSyncManagerTest, UnregisterSucceedsWithoutMainFrame) { - EXPECT_TRUE(Register(sync_options_1_)); - test_background_sync_manager_->set_has_main_frame_provider_host(false); - EXPECT_TRUE(Unregister(callback_registration_handle_.get())); - EXPECT_FALSE(GetRegistration(sync_options_1_)); -} - TEST_F(BackgroundSyncManagerTest, DefaultParameters) { - *test_controller_->background_sync_parameters() = BackgroundSyncParameters(); + *GetController()->background_sync_parameters() = BackgroundSyncParameters(); // Restart the BackgroundSyncManager so that it updates its parameters. SetupBackgroundSyncManager(); @@ -1749,7 +1031,7 @@ TEST_F(BackgroundSyncManagerTest, DefaultParameters) { TEST_F(BackgroundSyncManagerTest, OverrideParameters) { BackgroundSyncParameters* parameters = - test_controller_->background_sync_parameters(); + GetController()->background_sync_parameters(); parameters->disable = true; parameters->max_sync_attempts = 100; parameters->initial_retry_delay = base::TimeDelta::FromMinutes(200); @@ -1773,7 +1055,7 @@ TEST_F(BackgroundSyncManagerTest, DisablingFromControllerKeepsRegistrations) { EXPECT_TRUE(Register(sync_options_1_)); BackgroundSyncParameters* parameters = - test_controller_->background_sync_parameters(); + GetController()->background_sync_parameters(); parameters->disable = true; // Restart the BackgroundSyncManager so that it updates its parameters. @@ -1790,7 +1072,7 @@ TEST_F(BackgroundSyncManagerTest, DisablingFromControllerKeepsRegistrations) { TEST_F(BackgroundSyncManagerTest, DisabledPermanently) { BackgroundSyncParameters* parameters = - test_controller_->background_sync_parameters(); + GetController()->background_sync_parameters(); parameters->disable = true; // Restart the BackgroundSyncManager so that it updates its parameters. @@ -1810,11 +1092,11 @@ TEST_F(BackgroundSyncManagerTest, DisabledPermanently) { TEST_F(BackgroundSyncManagerTest, NotifyBackgroundSyncRegistered) { // Verify that the BackgroundSyncController is informed of registrations. - EXPECT_EQ(0, test_controller_->registration_count()); + EXPECT_EQ(0, GetController()->registration_count()); EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_EQ(1, test_controller_->registration_count()); + EXPECT_EQ(1, GetController()->registration_count()); EXPECT_EQ(GURL(kPattern1).GetOrigin().spec(), - test_controller_->registration_origin().spec()); + GetController()->registration_origin().spec()); } TEST_F(BackgroundSyncManagerTest, WakeBrowserCalled) { @@ -1822,30 +1104,30 @@ TEST_F(BackgroundSyncManagerTest, WakeBrowserCalled) { // The BackgroundSyncManager should declare in initialization // that it doesn't need to be woken up since it has no registrations. - EXPECT_LT(0, test_controller_->run_in_background_count()); - EXPECT_FALSE(test_controller_->run_in_background_enabled()); + EXPECT_LT(0, GetController()->run_in_background_count()); + EXPECT_FALSE(GetController()->run_in_background_enabled()); SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); - EXPECT_FALSE(test_controller_->run_in_background_enabled()); + EXPECT_FALSE(GetController()->run_in_background_enabled()); // Register a one-shot but it can't fire due to lack of network, wake up is // required. Register(sync_options_1_); - EXPECT_TRUE(test_controller_->run_in_background_enabled()); + EXPECT_TRUE(GetController()->run_in_background_enabled()); // Start the event but it will pause mid-sync due to // InitDelayedSyncEventTest() above. SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); - EXPECT_TRUE(test_controller_->run_in_background_enabled()); + EXPECT_TRUE(GetController()->run_in_background_enabled()); EXPECT_EQ(test_background_sync_manager_->background_sync_parameters() ->min_sync_recovery_time, base::TimeDelta::FromMilliseconds( - test_controller_->run_in_background_min_ms())); + GetController()->run_in_background_min_ms())); // Finish the sync. sync_fired_callback_.Run(SERVICE_WORKER_OK); base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(test_controller_->run_in_background_enabled()); + EXPECT_FALSE(GetController()->run_in_background_enabled()); } TEST_F(BackgroundSyncManagerTest, OneAttempt) { @@ -2022,7 +1304,7 @@ TEST_F(BackgroundSyncManagerTest, LastChance) { InitFailedSyncEventTest(); EXPECT_TRUE(Register(sync_options_1_)); - EXPECT_EQ(BACKGROUND_SYNC_EVENT_LAST_CHANCE_IS_NOT_LAST_CHANCE, + EXPECT_EQ(mojom::BackgroundSyncEventLastChance::IS_NOT_LAST_CHANCE, test_background_sync_manager_->last_chance()); EXPECT_TRUE(GetRegistration(sync_options_1_)); @@ -2031,7 +1313,7 @@ TEST_F(BackgroundSyncManagerTest, LastChance) { test_background_sync_manager_->delayed_task().Run(); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(GetRegistration(sync_options_1_)); - EXPECT_EQ(BACKGROUND_SYNC_EVENT_LAST_CHANCE_IS_LAST_CHANCE, + EXPECT_EQ(mojom::BackgroundSyncEventLastChance::IS_LAST_CHANCE, test_background_sync_manager_->last_chance()); } diff --git a/chromium/content/browser/background_sync/background_sync_metrics.cc b/chromium/content/browser/background_sync/background_sync_metrics.cc index 20b3a6651a3..8ae07b74eb6 100644 --- a/chromium/content/browser/background_sync/background_sync_metrics.cc +++ b/chromium/content/browser/background_sync/background_sync_metrics.cc @@ -33,40 +33,18 @@ ResultPattern EventResultToResultPattern(bool success, namespace content { // static -void BackgroundSyncMetrics::RecordEventStarted(SyncPeriodicity periodicity, - bool started_in_foreground) { - switch (periodicity) { - case SYNC_ONE_SHOT: - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Event.OneShotStartedInForeground", - started_in_foreground); - return; - case SYNC_PERIODIC: - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Event.PeriodicStartedInForeground", - started_in_foreground); - return; - } - NOTREACHED(); +void BackgroundSyncMetrics::RecordEventStarted(bool started_in_foreground) { + UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Event.OneShotStartedInForeground", + started_in_foreground); } // static -void BackgroundSyncMetrics::RecordEventResult(SyncPeriodicity periodicity, - bool success, +void BackgroundSyncMetrics::RecordEventResult(bool success, bool finished_in_foreground) { - switch (periodicity) { - case SYNC_ONE_SHOT: - UMA_HISTOGRAM_ENUMERATION( - "BackgroundSync.Event.OneShotResultPattern", - EventResultToResultPattern(success, finished_in_foreground), - RESULT_PATTERN_MAX + 1); - return; - case SYNC_PERIODIC: - UMA_HISTOGRAM_ENUMERATION( - "BackgroundSync.Event.PeriodicResultPattern", - EventResultToResultPattern(success, finished_in_foreground), - RESULT_PATTERN_MAX + 1); - return; - } - NOTREACHED(); + UMA_HISTOGRAM_ENUMERATION( + "BackgroundSync.Event.OneShotResultPattern", + EventResultToResultPattern(success, finished_in_foreground), + RESULT_PATTERN_MAX + 1); } // static @@ -84,62 +62,23 @@ void BackgroundSyncMetrics::RecordBatchSyncEventComplete( // static void BackgroundSyncMetrics::CountRegisterSuccess( - SyncPeriodicity periodicity, RegistrationCouldFire registration_could_fire, RegistrationIsDuplicate registration_is_duplicate) { - switch (periodicity) { - case SYNC_ONE_SHOT: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", - BACKGROUND_SYNC_STATUS_OK, - BACKGROUND_SYNC_STATUS_MAX + 1); - UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Registration.OneShot.CouldFire", - registration_could_fire == REGISTRATION_COULD_FIRE); - UMA_HISTOGRAM_BOOLEAN( - "BackgroundSync.Registration.OneShot.IsDuplicate", - registration_is_duplicate == REGISTRATION_IS_DUPLICATE); - return; - case SYNC_PERIODIC: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.Periodic", - BACKGROUND_SYNC_STATUS_OK, - BACKGROUND_SYNC_STATUS_MAX + 1); - UMA_HISTOGRAM_BOOLEAN( - "BackgroundSync.Registration.Periodic.IsDuplicate", - registration_is_duplicate == REGISTRATION_IS_DUPLICATE); - return; - } - NOTREACHED(); -} - -// static -void BackgroundSyncMetrics::CountRegisterFailure(SyncPeriodicity periodicity, - BackgroundSyncStatus result) { - switch (periodicity) { - case SYNC_ONE_SHOT: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", result, - BACKGROUND_SYNC_STATUS_MAX + 1); - return; - case SYNC_PERIODIC: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.Periodic", result, - BACKGROUND_SYNC_STATUS_MAX + 1); - return; - } - NOTREACHED(); + UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", + BACKGROUND_SYNC_STATUS_OK, + BACKGROUND_SYNC_STATUS_MAX + 1); + UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Registration.OneShot.CouldFire", + registration_could_fire == REGISTRATION_COULD_FIRE); + UMA_HISTOGRAM_BOOLEAN("BackgroundSync.Registration.OneShot.IsDuplicate", + registration_is_duplicate == REGISTRATION_IS_DUPLICATE); + return; } // static -void BackgroundSyncMetrics::CountUnregister(SyncPeriodicity periodicity, - BackgroundSyncStatus result) { - switch (periodicity) { - case SYNC_ONE_SHOT: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Unregistration.OneShot", result, - BACKGROUND_SYNC_STATUS_MAX + 1); - return; - case SYNC_PERIODIC: - UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Unregistration.Periodic", - result, BACKGROUND_SYNC_STATUS_MAX + 1); - return; - } - NOTREACHED(); +void BackgroundSyncMetrics::CountRegisterFailure(BackgroundSyncStatus result) { + UMA_HISTOGRAM_ENUMERATION("BackgroundSync.Registration.OneShot", result, + BACKGROUND_SYNC_STATUS_MAX + 1); + return; } } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_metrics.h b/chromium/content/browser/background_sync/background_sync_metrics.h index f92a7c5b7a3..3280896f671 100644 --- a/chromium/content/browser/background_sync/background_sync_metrics.h +++ b/chromium/content/browser/background_sync/background_sync_metrics.h @@ -29,13 +29,10 @@ class BackgroundSyncMetrics { }; // Records the start of a sync event. - static void RecordEventStarted(SyncPeriodicity periodicity, - bool startedin_foreground); + static void RecordEventStarted(bool startedin_foreground); // Records the result of a single sync event firing. - static void RecordEventResult(SyncPeriodicity periodicity, - bool result, - bool finished_in_foreground); + static void RecordEventResult(bool result, bool finished_in_foreground); // Records the result of running a batch of sync events, including the total // time spent, and the batch size. @@ -46,17 +43,11 @@ class BackgroundSyncMetrics { // indicates whether the conditions were sufficient for the sync to fire // immediately at the time it was registered. static void CountRegisterSuccess( - SyncPeriodicity periodicity, RegistrationCouldFire could_fire, RegistrationIsDuplicate registration_is_duplicate); // Records the status of a failed sync registration. - static void CountRegisterFailure(SyncPeriodicity periodicity, - BackgroundSyncStatus status); - - // Records the result of trying to unregister a sync. - static void CountUnregister(SyncPeriodicity periodicity, - BackgroundSyncStatus result); + static void CountRegisterFailure(BackgroundSyncStatus status); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BackgroundSyncMetrics); diff --git a/chromium/content/browser/background_sync/background_sync_power_observer.cc b/chromium/content/browser/background_sync/background_sync_power_observer.cc deleted file mode 100644 index 087fe21e37d..00000000000 --- a/chromium/content/browser/background_sync/background_sync_power_observer.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/background_sync/background_sync_power_observer.h" - -#include "base/power_monitor/power_monitor.h" -#include "base/thread_task_runner_handle.h" - -namespace content { - -BackgroundSyncPowerObserver::BackgroundSyncPowerObserver( - const base::Closure& power_callback) - : observing_power_monitor_(false), - on_battery_(true), - power_changed_callback_(power_callback) { - base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); - if (power_monitor) { - observing_power_monitor_ = true; - on_battery_ = power_monitor->IsOnBatteryPower(); - power_monitor->AddObserver(this); - } -} - -BackgroundSyncPowerObserver::~BackgroundSyncPowerObserver() { - base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); - - if (power_monitor) - power_monitor->RemoveObserver(this); -} - -bool BackgroundSyncPowerObserver::PowerSufficient( - SyncPowerState power_state) const { - DCHECK(observing_power_monitor_); - DCHECK(base::PowerMonitor::Get()); - - switch (power_state) { - case POWER_STATE_AUTO: - // TODO(jkarlin): Also check for device status, such as power saving mode - // or user preferences. crbug.com/482088. - return true; - case POWER_STATE_AVOID_DRAINING: - return !on_battery_; - } - - NOTREACHED(); - return false; -} - -void BackgroundSyncPowerObserver::OnPowerStateChange(bool on_battery_power) { - DCHECK(observing_power_monitor_); - DCHECK(base::PowerMonitor::Get()); - - if (on_battery_ == on_battery_power) - return; - - on_battery_ = on_battery_power; - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - power_changed_callback_); -} - -} // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_power_observer.h b/chromium/content/browser/background_sync/background_sync_power_observer.h deleted file mode 100644 index b0da8f9facc..00000000000 --- a/chromium/content/browser/background_sync/background_sync_power_observer.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_POWER_OBSERVER_H_ -#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_POWER_OBSERVER_H_ - -#include "base/bind.h" -#include "base/macros.h" -#include "base/power_monitor/power_observer.h" -#include "content/browser/background_sync/background_sync.pb.h" -#include "content/common/content_export.h" - -namespace content { - -// BackgroundSyncPowerObserver monitors the charging status of the device and -// determines if the power conditions of BackgroundSyncRegistrations are met. -class CONTENT_EXPORT BackgroundSyncPowerObserver : public base::PowerObserver { - public: - // Creates a BackgroundSyncPowerObserver. |power_callback| is run when the - // battery states changes asynchronously via PostMessage. - explicit BackgroundSyncPowerObserver(const base::Closure& power_callback); - - ~BackgroundSyncPowerObserver() override; - - // Returns true if the state of the battery (charging or not) meets the needs - // of |power_state|. - bool PowerSufficient(SyncPowerState power_state) const; - - private: - friend class BackgroundSyncPowerObserverTest; - - // PowerObserver overrides - void OnPowerStateChange(bool on_battery_power) override; - - // |observing_power_monitor_| is true when the constructor is able to find and - // register an observer with the base::PowerMonitor. This should always be - // true except for tests in which the browser initialization isn't done. - bool observing_power_monitor_; - - bool on_battery_; - - // The callback to run when the battery state changes. - base::Closure power_changed_callback_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundSyncPowerObserver); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_POWER_OBSERVER_H_ diff --git a/chromium/content/browser/background_sync/background_sync_power_observer_unittest.cc b/chromium/content/browser/background_sync/background_sync_power_observer_unittest.cc deleted file mode 100644 index 857ac069192..00000000000 --- a/chromium/content/browser/background_sync/background_sync_power_observer_unittest.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/test/power_monitor_test_base.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -namespace { - -class BackgroundSyncPowerObserverTest : public testing::Test { - protected: - BackgroundSyncPowerObserverTest() : power_changed_count_(0) { - power_monitor_source_ = new base::PowerMonitorTestSource(); - power_monitor_.reset(new base::PowerMonitor( - scoped_ptr(power_monitor_source_))); - power_observer_.reset(new BackgroundSyncPowerObserver( - base::Bind(&BackgroundSyncPowerObserverTest::OnPowerChanged, - base::Unretained(this)))); - } - - void SetOnBatteryPower(bool on_battery_power) { - power_monitor_source_->GeneratePowerStateEvent(on_battery_power); - } - - void OnPowerChanged() { power_changed_count_++; } - - // power_monitor_source_ is owned by power_monitor_ - base::PowerMonitorTestSource* power_monitor_source_; - scoped_ptr power_monitor_; - scoped_ptr power_observer_; - int power_changed_count_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundSyncPowerObserverTest); -}; - -TEST_F(BackgroundSyncPowerObserverTest, PowerChangeInvokesCallback) { - SetOnBatteryPower(true); - power_changed_count_ = 0; - - SetOnBatteryPower(false); - EXPECT_EQ(1, power_changed_count_); - SetOnBatteryPower(true); - EXPECT_EQ(2, power_changed_count_); - SetOnBatteryPower(true); - EXPECT_EQ(2, power_changed_count_); -} - -TEST_F(BackgroundSyncPowerObserverTest, PowerSufficientAuto) { - SetOnBatteryPower(false); - EXPECT_TRUE(power_observer_->PowerSufficient(POWER_STATE_AUTO)); - - SetOnBatteryPower(true); - EXPECT_TRUE(power_observer_->PowerSufficient(POWER_STATE_AUTO)); -} - -TEST_F(BackgroundSyncPowerObserverTest, PowerSufficientAvoidDraining) { - SetOnBatteryPower(false); - EXPECT_TRUE(power_observer_->PowerSufficient(POWER_STATE_AVOID_DRAINING)); - - SetOnBatteryPower(true); - EXPECT_FALSE(power_observer_->PowerSufficient(POWER_STATE_AVOID_DRAINING)); -} - -} // namespace - -} // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_registration.cc b/chromium/content/browser/background_sync/background_sync_registration.cc index b20e2ef744f..d835e67d4cb 100644 --- a/chromium/content/browser/background_sync/background_sync_registration.cc +++ b/chromium/content/browser/background_sync/background_sync_registration.cc @@ -18,9 +18,6 @@ const BackgroundSyncRegistration::RegistrationId const BackgroundSyncRegistration::RegistrationId BackgroundSyncRegistration::kInitialId = 0; -BackgroundSyncRegistration::BackgroundSyncRegistration() = default; -BackgroundSyncRegistration::~BackgroundSyncRegistration() = default; - bool BackgroundSyncRegistration::Equals( const BackgroundSyncRegistration& other) const { return options_.Equals(other.options_); @@ -30,69 +27,16 @@ bool BackgroundSyncRegistration::IsValid() const { return id_ != kInvalidRegistrationId; } -void BackgroundSyncRegistration::AddFinishedCallback( - const StateCallback& callback) { - DCHECK(!HasCompleted()); - notify_finished_callbacks_.push_back(callback); -} - -void BackgroundSyncRegistration::RunFinishedCallbacks() { - DCHECK(HasCompleted()); - - for (auto& callback : notify_finished_callbacks_) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, sync_state_)); - } - - notify_finished_callbacks_.clear(); -} - -bool BackgroundSyncRegistration::HasCompleted() const { - switch (sync_state_) { - case BACKGROUND_SYNC_STATE_PENDING: - case BACKGROUND_SYNC_STATE_FIRING: - case BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING: - case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING: - return false; - case BACKGROUND_SYNC_STATE_FAILED: - case BACKGROUND_SYNC_STATE_SUCCESS: - case BACKGROUND_SYNC_STATE_UNREGISTERED: - return true; - } - NOTREACHED(); - return false; -} - bool BackgroundSyncRegistration::IsFiring() const { switch (sync_state_) { - case BACKGROUND_SYNC_STATE_FIRING: - case BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING: - case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING: + case mojom::BackgroundSyncState::FIRING: + case mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING: return true; - case BACKGROUND_SYNC_STATE_PENDING: - case BACKGROUND_SYNC_STATE_FAILED: - case BACKGROUND_SYNC_STATE_SUCCESS: - case BACKGROUND_SYNC_STATE_UNREGISTERED: + case mojom::BackgroundSyncState::PENDING: return false; } NOTREACHED(); return false; } -void BackgroundSyncRegistration::SetUnregisteredState() { - DCHECK(!HasCompleted()); - - bool is_firing = IsFiring(); - - sync_state_ = is_firing ? BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING - : BACKGROUND_SYNC_STATE_UNREGISTERED; - - if (!is_firing) { - // If the registration is currently firing then wait to run - // RunFinishedCallbacks until after it has finished as it might - // change state to SUCCESS first. - RunFinishedCallbacks(); - } -} - } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_registration.h b/chromium/content/browser/background_sync/background_sync_registration.h index 4e4f1cd3f33..924540f65ac 100644 --- a/chromium/content/browser/background_sync/background_sync_registration.h +++ b/chromium/content/browser/background_sync/background_sync_registration.h @@ -24,34 +24,27 @@ namespace content { class CONTENT_EXPORT BackgroundSyncRegistration { public: using RegistrationId = int64_t; - using StateCallback = base::Callback; static const RegistrationId kInitialId; - BackgroundSyncRegistration(); - ~BackgroundSyncRegistration(); + BackgroundSyncRegistration() = default; + BackgroundSyncRegistration(const BackgroundSyncRegistration& other) = default; + BackgroundSyncRegistration& operator=( + const BackgroundSyncRegistration& other) = default; + ~BackgroundSyncRegistration() = default; bool Equals(const BackgroundSyncRegistration& other) const; bool IsValid() const; - void AddFinishedCallback(const StateCallback& callback); - void RunFinishedCallbacks(); - bool HasCompleted() const; bool IsFiring() const; - // If the registration is currently firing, sets its state to - // BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING. If it is firing, it sets - // the state to BACKGROUND_SYNC_STATE_UNREGISTERED and calls - // RunFinishedCallbacks. - void SetUnregisteredState(); - const BackgroundSyncRegistrationOptions* options() const { return &options_; } BackgroundSyncRegistrationOptions* options() { return &options_; } RegistrationId id() const { return id_; } void set_id(RegistrationId id) { id_ = id; } - BackgroundSyncState sync_state() const { return sync_state_; } - void set_sync_state(BackgroundSyncState state) { sync_state_ = state; } + mojom::BackgroundSyncState sync_state() const { return sync_state_; } + void set_sync_state(mojom::BackgroundSyncState state) { sync_state_ = state; } int num_attempts() const { return num_attempts_; } void set_num_attempts(int num_attempts) { num_attempts_ = num_attempts; } @@ -64,13 +57,9 @@ class CONTENT_EXPORT BackgroundSyncRegistration { BackgroundSyncRegistrationOptions options_; RegistrationId id_ = kInvalidRegistrationId; - BackgroundSyncState sync_state_ = BACKGROUND_SYNC_STATE_PENDING; + mojom::BackgroundSyncState sync_state_ = mojom::BackgroundSyncState::PENDING; int num_attempts_ = 0; base::Time delay_until_; - - std::list notify_finished_callbacks_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRegistration); }; } // namespace content @@ -80,15 +69,15 @@ namespace mojo { template <> struct CONTENT_EXPORT TypeConverter, - content::SyncRegistrationPtr> { + content::mojom::SyncRegistrationPtr> { static scoped_ptr Convert( - const content::SyncRegistrationPtr& input); + const content::mojom::SyncRegistrationPtr& input); }; template <> -struct CONTENT_EXPORT TypeConverter { - static content::SyncRegistrationPtr Convert( + static content::mojom::SyncRegistrationPtr Convert( const content::BackgroundSyncRegistration& input); }; diff --git a/chromium/content/browser/background_sync/background_sync_registration_handle.cc b/chromium/content/browser/background_sync/background_sync_registration_handle.cc deleted file mode 100644 index eca0abbb693..00000000000 --- a/chromium/content/browser/background_sync/background_sync_registration_handle.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/background_sync/background_sync_registration_handle.h" - -#include "content/browser/background_sync/background_sync_manager.h" -#include "content/public/browser/browser_thread.h" - -namespace content { - -BackgroundSyncRegistrationHandle::~BackgroundSyncRegistrationHandle() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (IsValid() && background_sync_manager_) - background_sync_manager_->ReleaseRegistrationHandle(handle_id_); -} - -void BackgroundSyncRegistrationHandle::Unregister( - int64_t sw_registration_id, - const StatusCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(IsValid()); - DCHECK(background_sync_manager_); - - background_sync_manager_->Unregister(sw_registration_id, handle_id_, - callback); -} - -void BackgroundSyncRegistrationHandle::NotifyWhenFinished( - const StatusAndStateCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(IsValid()); - DCHECK(background_sync_manager_); - - background_sync_manager_->NotifyWhenFinished(handle_id_, callback); -} - -bool BackgroundSyncRegistrationHandle::IsValid() const { - return registration_ != nullptr; -} - -BackgroundSyncRegistrationHandle::BackgroundSyncRegistrationHandle( - base::WeakPtr background_sync_manager, - HandleId handle_id) - : background_sync_manager_(background_sync_manager), - handle_id_(handle_id), - registration_( - background_sync_manager_->GetRegistrationForHandle(handle_id_)) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(background_sync_manager_); -} - -} // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_registration_handle.h b/chromium/content/browser/background_sync/background_sync_registration_handle.h deleted file mode 100644 index 8b2b5a92bb9..00000000000 --- a/chromium/content/browser/background_sync/background_sync_registration_handle.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HANDLE_H_ -#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HANDLE_H_ - -#include - -#include "base/callback.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "content/browser/background_sync/background_sync_registration.h" -#include "content/browser/background_sync/background_sync_status.h" -#include "content/common/background_sync_service.mojom.h" -#include "content/common/content_export.h" - -namespace content { - -class BackgroundSyncManager; - -// Handle to BackgroundSyncRegistration that is exposed to clients. Each -// BackgroundSyncRegistrationHandle is given a unique handle id (by the -// BackgroundSyncManager) which is released at destruction. -// BackgroundSyncRegistrationHandle objects must not be used (but may be -// destroyed) after the BackgroundSyncManager has been deleted. -class CONTENT_EXPORT BackgroundSyncRegistrationHandle { - public: - using HandleId = int64_t; - using StatusCallback = base::Callback; - using StatusAndStateCallback = - base::Callback; - - ~BackgroundSyncRegistrationHandle(); - - const BackgroundSyncRegistrationOptions* options() const { - DCHECK(background_sync_manager_); - return registration_->options(); - } - - BackgroundSyncState sync_state() const { - DCHECK(background_sync_manager_); - return registration_->sync_state(); - } - - // Unregisters the background sync registration. Calls |callback| - // with BACKGROUND_SYNC_STATUS_OK if it succeeds. - void Unregister(int64_t service_worker_id, const StatusCallback& callback); - - // Runs |callback| when the registration associated with |handle_id| - // completes.The provided status is BACKGROUND_SYNC_STATUS_OK if the operation - // succeeded. The provided state is BACKGROUND_SYNC_STATE_SUCCESS on success, - // BACKGRUOND_SYNC_STATE_FAILED on final failure, and - // BACKGROUND_SYNC_STATE_UNREGISTERED if the registration was unregistered - // before it could complete. NotifyWhenFinished should only be called for - // SYNC_ONE_SHOT registrations. - void NotifyWhenFinished(const StatusAndStateCallback& callback); - - // Returns true if the handle is backed by a BackgroundSyncRegistration in the - // BackgroundSyncManager. - bool IsValid() const; - - HandleId handle_id() const { return handle_id_; } - - private: - friend class BackgroundSyncManager; - - BackgroundSyncRegistrationHandle( - base::WeakPtr background_sync_manager, - HandleId handle_id); - - BackgroundSyncRegistration* registration() { - DCHECK(background_sync_manager_); - return registration_; - } - - // The BackgroundSyncManager is expected to remain alive for all operations - // except for possibly at destruction. - base::WeakPtr background_sync_manager_; - - // Each BackgroundSyncRegistrationHandle is assigned a unique handle id. - // The BackgroundSyncManager maps the id to an internal pointer. - HandleId handle_id_; - - // This is owned by background_sync_manager_ and is valid until handle_id_ is - // released in the destructor or background_sync_manager_ has been destroyed. - BackgroundSyncRegistration* registration_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRegistrationHandle); -}; - -} // namespace - -#endif // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HANDLE_H_ diff --git a/chromium/content/browser/background_sync/background_sync_registration_options.cc b/chromium/content/browser/background_sync/background_sync_registration_options.cc index 74b8bac8ef7..7b8e1e7e7bc 100644 --- a/chromium/content/browser/background_sync/background_sync_registration_options.cc +++ b/chromium/content/browser/background_sync/background_sync_registration_options.cc @@ -8,9 +8,7 @@ namespace content { bool BackgroundSyncRegistrationOptions::Equals( const BackgroundSyncRegistrationOptions& other) const { - return tag == other.tag && min_period == other.min_period && - network_state == other.network_state && - power_state == other.power_state && periodicity == other.periodicity; + return tag == other.tag && network_state == other.network_state; } } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_registration_options.h b/chromium/content/browser/background_sync/background_sync_registration_options.h index 0429f2a90cc..5c81e308846 100644 --- a/chromium/content/browser/background_sync/background_sync_registration_options.h +++ b/chromium/content/browser/background_sync/background_sync_registration_options.h @@ -19,10 +19,7 @@ struct CONTENT_EXPORT BackgroundSyncRegistrationOptions { bool Equals(const BackgroundSyncRegistrationOptions& other) const; std::string tag; - int64_t min_period = 0; SyncNetworkState network_state = NETWORK_STATE_ONLINE; - SyncPowerState power_state = POWER_STATE_AVOID_DRAINING; - SyncPeriodicity periodicity = SYNC_ONE_SHOT; }; } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_service_impl.cc b/chromium/content/browser/background_sync/background_sync_service_impl.cc index ffab6abc995..a5b51bd2edf 100644 --- a/chromium/content/browser/background_sync/background_sync_service_impl.cc +++ b/chromium/content/browser/background_sync/background_sync_service_impl.cc @@ -6,7 +6,6 @@ #include -#include "background_sync_registration_handle.h" #include "base/memory/weak_ptr.h" #include "base/stl_util.h" #include "content/browser/background_sync/background_sync_context_impl.h" @@ -20,28 +19,20 @@ namespace { // specializations. BackgroundSyncRegistrationOptions ToBackgroundSyncRegistrationOptions( - const SyncRegistrationPtr& in) { + const mojom::SyncRegistrationPtr& in) { BackgroundSyncRegistrationOptions out; out.tag = in->tag; - out.min_period = in->min_period_ms; - out.power_state = static_cast(in->power_state); out.network_state = static_cast(in->network_state); - out.periodicity = static_cast(in->periodicity); return out; } -SyncRegistrationPtr ToMojoRegistration( - const BackgroundSyncRegistrationHandle& in) { - SyncRegistrationPtr out(content::SyncRegistration::New()); - out->handle_id = in.handle_id(); +mojom::SyncRegistrationPtr ToMojoRegistration( + const BackgroundSyncRegistration& in) { + mojom::SyncRegistrationPtr out(content::mojom::SyncRegistration::New()); + out->id = in.id(); out->tag = in.options()->tag; - out->min_period_ms = in.options()->min_period; - out->periodicity = static_cast( - in.options()->periodicity); - out->power_state = - static_cast(in.options()->power_state); - out->network_state = static_cast( + out->network_state = static_cast( in.options()->network_state); return out; } @@ -54,42 +45,30 @@ SyncRegistrationPtr ToMojoRegistration( "mojo and manager enums must match") // TODO(iclelland): Move these tests somewhere else -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_NONE, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NONE, BACKGROUND_SYNC_STATUS_OK); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_STORAGE, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::STORAGE, BACKGROUND_SYNC_STATUS_STORAGE_ERROR); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_NOT_FOUND, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_FOUND, BACKGROUND_SYNC_STATUS_NOT_FOUND); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_NO_SERVICE_WORKER, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NO_SERVICE_WORKER, BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_NOT_ALLOWED, - BACKGROUND_SYNC_STATUS_NOT_ALLOWED); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_ERROR_MAX, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_ALLOWED, BACKGROUND_SYNC_STATUS_NOT_ALLOWED); +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::PERMISSION_DENIED, + BACKGROUND_SYNC_STATUS_PERMISSION_DENIED); +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::MAX, + BACKGROUND_SYNC_STATUS_PERMISSION_DENIED); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_NETWORK_STATE_ANY, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncNetworkState::ANY, SyncNetworkState::NETWORK_STATE_ANY); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_NETWORK_STATE_AVOID_CELLULAR, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncNetworkState::AVOID_CELLULAR, SyncNetworkState::NETWORK_STATE_AVOID_CELLULAR); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_NETWORK_STATE_ONLINE, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncNetworkState::ONLINE, SyncNetworkState::NETWORK_STATE_ONLINE); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_NETWORK_STATE_MAX, +COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncNetworkState::MAX, SyncNetworkState::NETWORK_STATE_ONLINE); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_POWER_STATE_AUTO, - SyncPowerState::POWER_STATE_AUTO); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_POWER_STATE_AVOID_DRAINING, - SyncPowerState::POWER_STATE_AVOID_DRAINING); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_POWER_STATE_MAX, - SyncPowerState::POWER_STATE_AVOID_DRAINING); - -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_PERIODICITY_PERIODIC, - SyncPeriodicity::SYNC_PERIODIC); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_PERIODICITY_ONE_SHOT, - SyncPeriodicity::SYNC_ONE_SHOT); -COMPILE_ASSERT_MATCHING_ENUM(BACKGROUND_SYNC_PERIODICITY_MAX, - SyncPeriodicity::SYNC_ONE_SHOT); - BackgroundSyncServiceImpl::~BackgroundSyncServiceImpl() { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(background_sync_context_->background_sync_manager()); @@ -97,7 +76,7 @@ BackgroundSyncServiceImpl::~BackgroundSyncServiceImpl() { BackgroundSyncServiceImpl::BackgroundSyncServiceImpl( BackgroundSyncContextImpl* background_sync_context, - mojo::InterfaceRequest request) + mojo::InterfaceRequest request) : background_sync_context_(background_sync_context), binding_(this, std::move(request)), weak_ptr_factory_(this) { @@ -114,60 +93,25 @@ void BackgroundSyncServiceImpl::OnConnectionError() { // |this| is now deleted. } -void BackgroundSyncServiceImpl::Register(content::SyncRegistrationPtr options, - int64_t sw_registration_id, - bool requested_from_service_worker, - const RegisterCallback& callback) { +void BackgroundSyncServiceImpl::Register( + content::mojom::SyncRegistrationPtr options, + int64_t sw_registration_id, + const RegisterCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - BackgroundSyncRegistrationOptions mgr_options = + BackgroundSyncRegistrationOptions manager_options = ToBackgroundSyncRegistrationOptions(options); BackgroundSyncManager* background_sync_manager = background_sync_context_->background_sync_manager(); DCHECK(background_sync_manager); background_sync_manager->Register( - sw_registration_id, mgr_options, requested_from_service_worker, - base::Bind(&BackgroundSyncServiceImpl::OnRegisterResult, - weak_ptr_factory_.GetWeakPtr(), callback)); -} - -void BackgroundSyncServiceImpl::Unregister( - BackgroundSyncRegistrationHandle::HandleId handle_id, - int64_t sw_registration_id, - const UnregisterCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - BackgroundSyncRegistrationHandle* registration = - active_handles_.Lookup(handle_id); - if (!registration) { - callback.Run(BACKGROUND_SYNC_ERROR_NOT_ALLOWED); - return; - } - - registration->Unregister( - sw_registration_id, - base::Bind(&BackgroundSyncServiceImpl::OnUnregisterResult, - weak_ptr_factory_.GetWeakPtr(), callback)); -} - -void BackgroundSyncServiceImpl::GetRegistration( - BackgroundSyncPeriodicity periodicity, - const mojo::String& tag, - int64_t sw_registration_id, - const GetRegistrationCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - BackgroundSyncManager* background_sync_manager = - background_sync_context_->background_sync_manager(); - DCHECK(background_sync_manager); - background_sync_manager->GetRegistration( - sw_registration_id, tag.get(), static_cast(periodicity), + sw_registration_id, manager_options, base::Bind(&BackgroundSyncServiceImpl::OnRegisterResult, weak_ptr_factory_.GetWeakPtr(), callback)); } void BackgroundSyncServiceImpl::GetRegistrations( - BackgroundSyncPeriodicity periodicity, int64_t sw_registration_id, const GetRegistrationsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -175,129 +119,43 @@ void BackgroundSyncServiceImpl::GetRegistrations( background_sync_context_->background_sync_manager(); DCHECK(background_sync_manager); background_sync_manager->GetRegistrations( - sw_registration_id, static_cast(periodicity), + sw_registration_id, base::Bind(&BackgroundSyncServiceImpl::OnGetRegistrationsResult, weak_ptr_factory_.GetWeakPtr(), callback)); } -void BackgroundSyncServiceImpl::GetPermissionStatus( - BackgroundSyncPeriodicity periodicity, - int64_t sw_registration_id, - const GetPermissionStatusCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // TODO(iclelland): Implement a real policy. This is a stub implementation. - // OneShot: crbug.com/482091 - // Periodic: crbug.com/482093 - callback.Run(BACKGROUND_SYNC_ERROR_NONE, PERMISSION_STATUS_GRANTED); -} - -void BackgroundSyncServiceImpl::DuplicateRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id, - const DuplicateRegistrationHandleCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - BackgroundSyncManager* background_sync_manager = - background_sync_context_->background_sync_manager(); - DCHECK(background_sync_manager); - - scoped_ptr registration_handle = - background_sync_manager->DuplicateRegistrationHandle(handle_id); - - BackgroundSyncRegistrationHandle* handle_ptr = registration_handle.get(); - - if (!registration_handle) { - callback.Run(BACKGROUND_SYNC_ERROR_NOT_FOUND, - SyncRegistrationPtr(content::SyncRegistration::New())); - return; - } - - active_handles_.AddWithID(registration_handle.release(), - handle_ptr->handle_id()); - SyncRegistrationPtr mojoResult = ToMojoRegistration(*handle_ptr); - callback.Run(BACKGROUND_SYNC_ERROR_NONE, std::move(mojoResult)); -} - -void BackgroundSyncServiceImpl::ReleaseRegistration( - BackgroundSyncRegistrationHandle::HandleId handle_id) { - if (!active_handles_.Lookup(handle_id)) { - // TODO(jkarlin): Abort client. - LOG(WARNING) << "Client attempted to release non-existing registration"; - return; - } - - active_handles_.Remove(handle_id); -} - -void BackgroundSyncServiceImpl::NotifyWhenFinished( - BackgroundSyncRegistrationHandle::HandleId handle_id, - const NotifyWhenFinishedCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - BackgroundSyncRegistrationHandle* registration = - active_handles_.Lookup(handle_id); - if (!registration) { - callback.Run(BACKGROUND_SYNC_ERROR_NOT_ALLOWED, - BACKGROUND_SYNC_STATE_FAILED); - return; - } - - registration->NotifyWhenFinished( - base::Bind(&BackgroundSyncServiceImpl::OnNotifyWhenFinishedResult, - weak_ptr_factory_.GetWeakPtr(), callback)); -} - void BackgroundSyncServiceImpl::OnRegisterResult( const RegisterCallback& callback, BackgroundSyncStatus status, - scoped_ptr result) { + scoped_ptr result) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - BackgroundSyncRegistrationHandle* result_ptr = result.get(); if (status != BACKGROUND_SYNC_STATUS_OK) { - callback.Run(static_cast(status), - SyncRegistrationPtr(content::SyncRegistration::New())); + callback.Run( + static_cast(status), + mojom::SyncRegistrationPtr(content::mojom::SyncRegistration::New())); return; } DCHECK(result); - active_handles_.AddWithID(result.release(), result_ptr->handle_id()); - SyncRegistrationPtr mojoResult = ToMojoRegistration(*result_ptr); - callback.Run(static_cast(status), + mojom::SyncRegistrationPtr mojoResult = ToMojoRegistration(*result); + callback.Run(static_cast(status), std::move(mojoResult)); } -void BackgroundSyncServiceImpl::OnUnregisterResult( - const UnregisterCallback& callback, - BackgroundSyncStatus status) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback.Run(static_cast(status)); -} - void BackgroundSyncServiceImpl::OnGetRegistrationsResult( const GetRegistrationsCallback& callback, BackgroundSyncStatus status, - scoped_ptr> - result_registrations) { + scoped_ptr> result_registrations) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(result_registrations); - mojo::Array mojo_registrations(0); - for (BackgroundSyncRegistrationHandle* registration : *result_registrations) { - active_handles_.AddWithID(registration, registration->handle_id()); + mojo::Array mojo_registrations; + for (const BackgroundSyncRegistration* registration : *result_registrations) mojo_registrations.push_back(ToMojoRegistration(*registration)); - } - result_registrations->weak_clear(); - - callback.Run(static_cast(status), + callback.Run(static_cast(status), std::move(mojo_registrations)); } -void BackgroundSyncServiceImpl::OnNotifyWhenFinishedResult( - const NotifyWhenFinishedCallback& callback, - BackgroundSyncStatus status, - BackgroundSyncState sync_state) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback.Run(static_cast(status), sync_state); -} - } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_service_impl.h b/chromium/content/browser/background_sync/background_sync_service_impl.h index a5b6d184e06..1851b90579a 100644 --- a/chromium/content/browser/background_sync/background_sync_service_impl.h +++ b/chromium/content/browser/background_sync/background_sync_service_impl.h @@ -20,56 +20,31 @@ namespace content { class BackgroundSyncContextImpl; class CONTENT_EXPORT BackgroundSyncServiceImpl - : public NON_EXPORTED_BASE(BackgroundSyncService) { + : public NON_EXPORTED_BASE(mojom::BackgroundSyncService) { public: BackgroundSyncServiceImpl( BackgroundSyncContextImpl* background_sync_context, - mojo::InterfaceRequest request); + mojo::InterfaceRequest request); ~BackgroundSyncServiceImpl() override; private: friend class BackgroundSyncServiceImplTest; - // BackgroundSyncService methods: - void Register(content::SyncRegistrationPtr options, + // mojom::BackgroundSyncService methods: + void Register(content::mojom::SyncRegistrationPtr options, int64_t sw_registration_id, - bool requested_from_service_worker, const RegisterCallback& callback) override; - void Unregister(BackgroundSyncRegistrationHandle::HandleId handle_id, - int64_t sw_registration_id, - const UnregisterCallback& callback) override; - void GetRegistration(BackgroundSyncPeriodicity periodicity, - const mojo::String& tag, - int64_t sw_registration_id, - const GetRegistrationCallback& callback) override; - void GetRegistrations(BackgroundSyncPeriodicity periodicity, - int64_t sw_registration_id, + void GetRegistrations(int64_t sw_registration_id, const GetRegistrationsCallback& callback) override; - void GetPermissionStatus( - BackgroundSyncPeriodicity periodicity, - int64_t sw_registration_id, - const GetPermissionStatusCallback& callback) override; - void DuplicateRegistrationHandle( - BackgroundSyncRegistrationHandle::HandleId handle_id, - const DuplicateRegistrationHandleCallback& callback) override; - void ReleaseRegistration( - BackgroundSyncRegistrationHandle::HandleId handle_id) override; - void NotifyWhenFinished(BackgroundSyncRegistrationHandle::HandleId handle_id, - const NotifyWhenFinishedCallback& callback) override; void OnRegisterResult(const RegisterCallback& callback, BackgroundSyncStatus status, - scoped_ptr result); - void OnUnregisterResult(const UnregisterCallback& callback, - BackgroundSyncStatus status); + scoped_ptr result); void OnGetRegistrationsResult( const GetRegistrationsCallback& callback, BackgroundSyncStatus status, - scoped_ptr> result); - void OnNotifyWhenFinishedResult(const NotifyWhenFinishedCallback& callback, - BackgroundSyncStatus status, - BackgroundSyncState sync_state); + scoped_ptr> result); // Called when an error is detected on binding_. void OnConnectionError(); @@ -77,12 +52,7 @@ class CONTENT_EXPORT BackgroundSyncServiceImpl // background_sync_context_ owns this. BackgroundSyncContextImpl* background_sync_context_; - mojo::Binding binding_; - - // The registrations that the client might reference. - IDMap active_handles_; + mojo::Binding binding_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc b/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc index a9f8307c04a..8f413508481 100644 --- a/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc +++ b/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc @@ -10,8 +10,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/scoped_ptr.h" -#include "base/power_monitor/power_monitor.h" -#include "base/power_monitor/power_monitor_source.h" #include "base/run_loop.h" #include "content/browser/background_sync/background_sync_context_impl.h" #include "content/browser/background_sync/background_sync_network_observer.h" @@ -19,9 +17,12 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/permission_type.h" #include "content/public/test/background_sync_test_util.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/mock_permission_manager.h" +#include "content/test/test_background_sync_context_impl.h" #include "mojo/public/cpp/bindings/interface_ptr.h" #include "net/base/network_change_notifier.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,6 +31,8 @@ namespace content { namespace { +using ::testing::_; + const char kServiceWorkerPattern[] = "https://example.com/a"; const char kServiceWorkerScript[] = "https://example.com/a/script.js"; @@ -54,51 +57,29 @@ void FindServiceWorkerRegistrationCallback( // Callbacks from BackgroundSyncServiceImpl methods -void ErrorAndRegistrationCallback(bool* called, - BackgroundSyncError* out_error, - SyncRegistrationPtr* out_registration, - BackgroundSyncError error, - const SyncRegistrationPtr& registration) { +void ErrorAndRegistrationCallback( + bool* called, + mojom::BackgroundSyncError* out_error, + mojom::SyncRegistrationPtr* out_registration, + mojom::BackgroundSyncError error, + const mojom::SyncRegistrationPtr& registration) { *called = true; *out_error = error; *out_registration = registration.Clone(); } -void ErrorAndStateCallback(bool* called, - BackgroundSyncError* out_error, - BackgroundSyncState* out_state, - BackgroundSyncError error, - BackgroundSyncState state) { - *called = true; - *out_error = error; - *out_state = state; -} - -void ErrorCallback(bool* called, - BackgroundSyncError* out_error, - BackgroundSyncError error) { - *called = true; - *out_error = error; -} - void ErrorAndRegistrationListCallback( bool* called, - BackgroundSyncError* out_error, + mojom::BackgroundSyncError* out_error, unsigned long* out_array_size, - BackgroundSyncError error, - mojo::Array registrations) { + mojom::BackgroundSyncError error, + mojo::Array registrations) { *called = true; *out_error = error; - if (error == BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE) + if (error == mojom::BackgroundSyncError::NONE) *out_array_size = registrations.size(); } -class MockPowerMonitorSource : public base::PowerMonitorSource { - private: - // PowerMonitorSource overrides. - bool IsOnBatteryPowerImpl() final { return false; } -}; - } // namespace class BackgroundSyncServiceImplTest : public testing::Test { @@ -107,7 +88,7 @@ class BackgroundSyncServiceImplTest : public testing::Test { : thread_bundle_( new TestBrowserThreadBundle(TestBrowserThreadBundle::IO_MAINLOOP)), network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) { - default_sync_registration_ = SyncRegistration::New(); + default_sync_registration_ = mojom::SyncRegistration::New(); } void SetUp() override { @@ -137,6 +118,14 @@ class BackgroundSyncServiceImplTest : public testing::Test { void CreateTestHelper() { embedded_worker_helper_.reset( new EmbeddedWorkerTestHelper(base::FilePath())); + scoped_ptr mock_permission_manager( + new testing::NiceMock()); + ON_CALL(*mock_permission_manager, + GetPermissionStatus(PermissionType::BACKGROUND_SYNC, _, _)) + .WillByDefault( + testing::Return(blink::mojom::PermissionStatus::GRANTED)); + embedded_worker_helper_->browser_context()->SetPermissionManager( + std::move(mock_permission_manager)); } void CreateStoragePartition() { @@ -145,16 +134,15 @@ class BackgroundSyncServiceImplTest : public testing::Test { storage_partition_impl_.reset(new StoragePartitionImpl( embedded_worker_helper_->browser_context(), base::FilePath(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)); + nullptr, nullptr, nullptr, nullptr, nullptr)); embedded_worker_helper_->context_wrapper()->set_storage_partition( storage_partition_impl_.get()); } void CreateBackgroundSyncContext() { - power_monitor_.reset( - new base::PowerMonitor(make_scoped_ptr(new MockPowerMonitorSource()))); - - background_sync_context_ = new BackgroundSyncContextImpl(); + // Registering for background sync includes a check for having a same-origin + // main frame. Use a test context that allows control over that check. + background_sync_context_ = new TestBackgroundSyncContextImpl(); background_sync_context_->Init(embedded_worker_helper_->context_wrapper()); // Tests do not expect the sync event to fire immediately after @@ -190,10 +178,10 @@ class BackgroundSyncServiceImplTest : public testing::Test { void CreateBackgroundSyncServiceImpl() { // Create a dummy mojo channel so that the BackgroundSyncServiceImpl can be - // instantiated - mojo::InterfaceRequest service_request = + // instantiated. + mojo::InterfaceRequest service_request = mojo::GetProxy(&service_ptr_); - // Create a new BackgroundSyncServiceImpl bound to the dummy channel + // Create a new BackgroundSyncServiceImpl bound to the dummy channel. background_sync_context_->CreateService(std::move(service_request)); base::RunLoop().RunUntilIdle(); @@ -202,44 +190,16 @@ class BackgroundSyncServiceImplTest : public testing::Test { } // Helpers for testing BackgroundSyncServiceImpl methods - void RegisterOneShot( - SyncRegistrationPtr sync, - const BackgroundSyncService::RegisterCallback& callback) { - service_impl_->Register(std::move(sync), sw_registration_id_, - false /* requested_from_service_worker */, - callback); - base::RunLoop().RunUntilIdle(); - } - - void UnregisterOneShot( - int32_t handle_id, - const BackgroundSyncService::UnregisterCallback& callback) { - service_impl_->Unregister( - handle_id, sw_registration_id_, callback); - base::RunLoop().RunUntilIdle(); - } - - void GetRegistrationOneShot( - const mojo::String& tag, - const BackgroundSyncService::RegisterCallback& callback) { - service_impl_->GetRegistration( - BackgroundSyncPeriodicity::BACKGROUND_SYNC_PERIODICITY_ONE_SHOT, tag, - sw_registration_id_, callback); - base::RunLoop().RunUntilIdle(); - } - - void GetRegistrationsOneShot( - const BackgroundSyncService::GetRegistrationsCallback& callback) { - service_impl_->GetRegistrations( - BackgroundSyncPeriodicity::BACKGROUND_SYNC_PERIODICITY_ONE_SHOT, - sw_registration_id_, callback); + void Register( + mojom::SyncRegistrationPtr sync, + const mojom::BackgroundSyncService::RegisterCallback& callback) { + service_impl_->Register(std::move(sync), sw_registration_id_, callback); base::RunLoop().RunUntilIdle(); } - void NotifyWhenDone( - int32_t handle_id, - const BackgroundSyncService::NotifyWhenFinishedCallback& callback) { - service_impl_->NotifyWhenFinished(handle_id, callback); + void GetRegistrations( + const mojom::BackgroundSyncService::GetRegistrationsCallback& callback) { + service_impl_->GetRegistrations(sw_registration_id_, callback); base::RunLoop().RunUntilIdle(); } @@ -247,154 +207,57 @@ class BackgroundSyncServiceImplTest : public testing::Test { scoped_ptr network_change_notifier_; scoped_ptr embedded_worker_helper_; scoped_ptr storage_partition_impl_; - scoped_ptr power_monitor_; scoped_refptr background_sync_context_; int64_t sw_registration_id_; scoped_refptr sw_registration_; - BackgroundSyncServicePtr service_ptr_; + mojom::BackgroundSyncServicePtr service_ptr_; BackgroundSyncServiceImpl* service_impl_; // Owned by background_sync_context_ - SyncRegistrationPtr default_sync_registration_; + mojom::SyncRegistrationPtr default_sync_registration_; }; // Tests TEST_F(BackgroundSyncServiceImplTest, Register) { bool called = false; - BackgroundSyncError error; - SyncRegistrationPtr reg; - RegisterOneShot( - default_sync_registration_.Clone(), - base::Bind(&ErrorAndRegistrationCallback, &called, &error, ®)); + mojom::BackgroundSyncError error; + mojom::SyncRegistrationPtr reg; + Register(default_sync_registration_.Clone(), + base::Bind(&ErrorAndRegistrationCallback, &called, &error, ®)); EXPECT_TRUE(called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, error); + EXPECT_EQ(mojom::BackgroundSyncError::NONE, error); EXPECT_EQ("", reg->tag); } -TEST_F(BackgroundSyncServiceImplTest, Unregister) { - bool unregister_called = false; - BackgroundSyncError unregister_error; - SyncRegistrationPtr reg; - UnregisterOneShot( - default_sync_registration_->handle_id, - base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); - EXPECT_TRUE(unregister_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NOT_ALLOWED, - unregister_error); -} - -TEST_F(BackgroundSyncServiceImplTest, UnregisterWithRegisteredSync) { - bool register_called = false; - bool unregister_called = false; - BackgroundSyncError register_error; - BackgroundSyncError unregister_error; - SyncRegistrationPtr reg; - RegisterOneShot(default_sync_registration_.Clone(), - base::Bind(&ErrorAndRegistrationCallback, ®ister_called, - ®ister_error, ®)); - EXPECT_TRUE(register_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, register_error); - UnregisterOneShot( - reg->handle_id, - base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); - EXPECT_TRUE(unregister_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, unregister_error); -} - -TEST_F(BackgroundSyncServiceImplTest, GetRegistration) { - bool called = false; - BackgroundSyncError error; - SyncRegistrationPtr reg; - GetRegistrationOneShot( - "", base::Bind(&ErrorAndRegistrationCallback, &called, &error, ®)); - EXPECT_TRUE(called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NOT_FOUND, error); -} - -TEST_F(BackgroundSyncServiceImplTest, GetRegistrationWithRegisteredSync) { - bool register_called = false; - bool getregistration_called = false; - BackgroundSyncError register_error; - BackgroundSyncError getregistration_error; - SyncRegistrationPtr register_reg; - SyncRegistrationPtr getregistration_reg; - RegisterOneShot(default_sync_registration_.Clone(), - base::Bind(&ErrorAndRegistrationCallback, ®ister_called, - ®ister_error, ®ister_reg)); - EXPECT_TRUE(register_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, register_error); - GetRegistrationOneShot( - register_reg->tag, - base::Bind(&ErrorAndRegistrationCallback, &getregistration_called, - &getregistration_error, &getregistration_reg)); - EXPECT_TRUE(getregistration_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, - getregistration_error); -} - TEST_F(BackgroundSyncServiceImplTest, GetRegistrations) { bool called = false; - BackgroundSyncError error; + mojom::BackgroundSyncError error; unsigned long array_size = 0UL; - GetRegistrationsOneShot(base::Bind(&ErrorAndRegistrationListCallback, &called, - &error, &array_size)); + GetRegistrations(base::Bind(&ErrorAndRegistrationListCallback, &called, + &error, &array_size)); EXPECT_TRUE(called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, error); + EXPECT_EQ(mojom::BackgroundSyncError::NONE, error); EXPECT_EQ(0UL, array_size); } TEST_F(BackgroundSyncServiceImplTest, GetRegistrationsWithRegisteredSync) { bool register_called = false; bool getregistrations_called = false; - BackgroundSyncError register_error; - BackgroundSyncError getregistrations_error; - SyncRegistrationPtr register_reg; + mojom::BackgroundSyncError register_error; + mojom::BackgroundSyncError getregistrations_error; + mojom::SyncRegistrationPtr register_reg; unsigned long array_size = 0UL; - RegisterOneShot(default_sync_registration_.Clone(), - base::Bind(&ErrorAndRegistrationCallback, ®ister_called, - ®ister_error, ®ister_reg)); + Register(default_sync_registration_.Clone(), + base::Bind(&ErrorAndRegistrationCallback, ®ister_called, + ®ister_error, ®ister_reg)); EXPECT_TRUE(register_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, register_error); - GetRegistrationsOneShot(base::Bind(&ErrorAndRegistrationListCallback, - &getregistrations_called, - &getregistrations_error, &array_size)); + EXPECT_EQ(mojom::BackgroundSyncError::NONE, register_error); + GetRegistrations(base::Bind(&ErrorAndRegistrationListCallback, + &getregistrations_called, &getregistrations_error, + &array_size)); EXPECT_TRUE(getregistrations_called); - EXPECT_EQ(BackgroundSyncError::BACKGROUND_SYNC_ERROR_NONE, - getregistrations_error); + EXPECT_EQ(mojom::BackgroundSyncError::NONE, getregistrations_error); EXPECT_EQ(1UL, array_size); } -TEST_F(BackgroundSyncServiceImplTest, NotifyWhenFinished) { - // Register a sync event. - bool register_called = false; - BackgroundSyncError register_error; - SyncRegistrationPtr reg; - RegisterOneShot(default_sync_registration_.Clone(), - base::Bind(&ErrorAndRegistrationCallback, ®ister_called, - ®ister_error, ®)); - EXPECT_TRUE(register_called); - EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, register_error); - - // Unregister it. - bool unregister_called = false; - BackgroundSyncError unregister_error; - UnregisterOneShot( - reg->handle_id, - base::Bind(&ErrorCallback, &unregister_called, &unregister_error)); - EXPECT_TRUE(unregister_called); - EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, unregister_error); - - // Call NotifyWhenDone and verify that it calls back with unregistered. - bool notify_done_called = false; - BackgroundSyncError notify_done_error = BACKGROUND_SYNC_ERROR_NONE; - BackgroundSyncState notify_done_sync_state = BACKGROUND_SYNC_STATE_SUCCESS; - - NotifyWhenDone(reg->handle_id, - base::Bind(&ErrorAndStateCallback, ¬ify_done_called, - ¬ify_done_error, ¬ify_done_sync_state)); - EXPECT_TRUE(notify_done_called); - EXPECT_EQ(BACKGROUND_SYNC_ERROR_NONE, notify_done_error); - EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, notify_done_sync_state); -} - } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_status.h b/chromium/content/browser/background_sync/background_sync_status.h index 3faee998377..27a8805325c 100644 --- a/chromium/content/browser/background_sync/background_sync_status.h +++ b/chromium/content/browser/background_sync/background_sync_status.h @@ -16,7 +16,8 @@ enum BackgroundSyncStatus { BACKGROUND_SYNC_STATUS_NOT_FOUND, BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER, BACKGROUND_SYNC_STATUS_NOT_ALLOWED, - BACKGROUND_SYNC_STATUS_MAX = BACKGROUND_SYNC_STATUS_NOT_ALLOWED + BACKGROUND_SYNC_STATUS_PERMISSION_DENIED, + BACKGROUND_SYNC_STATUS_MAX = BACKGROUND_SYNC_STATUS_PERMISSION_DENIED }; } // namespace content diff --git a/chromium/content/browser/bad_message.h b/chromium/content/browser/bad_message.h index 1a8c80abb4e..441b903e350 100644 --- a/chromium/content/browser/bad_message.h +++ b/chromium/content/browser/bad_message.h @@ -127,6 +127,19 @@ enum BadMessageReason { DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103, DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104, BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN = 105, + ACI_WRONG_STORAGE_PARTITION = 106, + RDHI_WRONG_STORAGE_PARTITION = 107, + RDH_INVALID_REQUEST_ID = 108, + BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN = 109, + WSH_SEND_BLOB_DURING_BLOB_SEND = 110, + WSH_SEND_FRAME_DURING_BLOB_SEND = 111, + RFH_UNEXPECTED_LOAD_START = 112, + NMF_INVALID_ARGUMENT = 113, + RFH_INVALID_ORIGIN_ON_COMMIT = 114, + BDH_UUID_REGISTERED = 115, + BDH_CONSTRUCTION_FAILED = 116, + BDH_INVALID_REFCOUNT_OPERATION = 117, + BDH_INVALID_URL_OPERATION = 118, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the diff --git a/chromium/content/browser/battery_status/battery_monitor_integration_browsertest.cc b/chromium/content/browser/battery_status/battery_monitor_integration_browsertest.cc index 468126f3d39..1aee295891b 100644 --- a/chromium/content/browser/battery_status/battery_monitor_integration_browsertest.cc +++ b/chromium/content/browser/battery_status/battery_monitor_integration_browsertest.cc @@ -95,6 +95,13 @@ class TestContentBrowserClient : public ContentBrowserClient { registry->AddService(base::Bind(&FakeBatteryMonitor::Create)); } + void AppendExtraCommandLineSwitches(base::CommandLine* command_line, + int child_process_id) override { + // Necessary for passing kIsolateSitesForTesting flag to the renderer. + ShellContentBrowserClient::Get()->AppendExtraCommandLineSwitches( + command_line, child_process_id); + } + #if defined(OS_ANDROID) void GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, diff --git a/chromium/content/browser/blob_storage/blob_async_builder_host_unittest.cc b/chromium/content/browser/blob_storage/blob_async_builder_host_unittest.cc index 934e0db887b..d40cc7cbc0c 100644 --- a/chromium/content/browser/blob_storage/blob_async_builder_host_unittest.cc +++ b/chromium/content/browser/blob_storage/blob_async_builder_host_unittest.cc @@ -11,14 +11,21 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/shared_memory.h" +#include "base/run_loop.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "storage/browser/blob/blob_data_builder.h" +#include "storage/browser/blob/blob_data_handle.h" +#include "storage/browser/blob/blob_storage_context.h" #include "storage/common/blob_storage/blob_storage_constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace storage { namespace { const std::string kBlobUUID = "blobUUIDYAY"; -const std::string kFakeBlobUUID = "fakeBlob"; -const std::string kBlobType = "blobtypeYAY"; +const std::string kContentType = "content_type"; +const std::string kContentDisposition = "content_disposition"; +const std::string kCompletedBlobUUID = "completedBlob"; +const std::string kCompletedBlobData = "completedBlobData"; const size_t kTestBlobStorageIPCThresholdBytes = 5; const size_t kTestBlobStorageMaxSharedMemoryBytes = 20; @@ -52,87 +59,81 @@ void AddShortcutMemoryItem(size_t length, BlobDataBuilder* out) { void AddBlobItem(std::vector* out) { DataElement blob; - blob.SetToBlob(kFakeBlobUUID); + blob.SetToBlob(kCompletedBlobUUID); out->push_back(blob); } +} // namespace class BlobAsyncBuilderHostTest : public testing::Test { - protected: + public: BlobAsyncBuilderHostTest() - : matching_builder_(nullptr), - done_called_(false), - cancel_called_(false), - cancel_code_(IPCBlobCreationCancelCode::UNKNOWN), + : cancel_code_(IPCBlobCreationCancelCode::UNKNOWN), request_called_(false) {} ~BlobAsyncBuilderHostTest() override {} void SetUp() override { - matching_builder_ = nullptr; - done_called_ = false; - cancel_called_ = false; cancel_code_ = IPCBlobCreationCancelCode::UNKNOWN; request_called_ = false; requests_.clear(); memory_handles_.clear(); - file_handles_.clear(); host_.SetMemoryConstantsForTesting(kTestBlobStorageIPCThresholdBytes, kTestBlobStorageMaxSharedMemoryBytes, kTestBlobStorageMaxFileSizeBytes); - } - - void SetMatchingBuilder(BlobDataBuilder* builder) { - matching_builder_ = builder; - } - - void CancelCallback(IPCBlobCreationCancelCode code) { - cancel_called_ = true; - cancel_code_ = code; - } - - void DoneCallback(const BlobDataBuilder& builder) { - // This does a deep comparison, including internal data items. - if (matching_builder_) - EXPECT_EQ(*matching_builder_, builder); - done_called_ = true; + BlobDataBuilder builder(kCompletedBlobUUID); + builder.AppendData(kCompletedBlobData); + completed_blob_handle_ = context_.AddFinishedBlob(builder); + completed_blob_uuid_set_ = {kCompletedBlobUUID}; } void RequestMemoryCallback( - const std::vector& requests, - const std::vector& shared_memory_handles, - const std::vector& file_sizes) { - this->requests_ = requests; - memory_handles_ = shared_memory_handles; - file_handles_ = file_sizes; + scoped_ptr> requests, + scoped_ptr> shared_memory_handles, + scoped_ptr> files) { + requests_ = std::move(*requests); + memory_handles_ = std::move(*shared_memory_handles); request_called_ = true; } - bool BuildBlobAsync(const std::vector& descriptions, - size_t memory_available) { - done_called_ = false; - cancel_called_ = false; + BlobTransportResult BuildBlobAsync( + const std::vector& descriptions, + const std::set& referenced_blob_uuids, + size_t memory_available) { request_called_ = false; + BlobTransportResult register_result = + host_.RegisterBlobUUID(kBlobUUID, kContentType, kContentDisposition, + referenced_blob_uuids, &context_); + if (register_result != BlobTransportResult::DONE) { + return register_result; + } return host_.StartBuildingBlob( - kBlobUUID, kBlobType, descriptions, memory_available, + kBlobUUID, descriptions, memory_available, &context_, base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, - base::Unretained(this)), - base::Bind(&BlobAsyncBuilderHostTest::DoneCallback, - base::Unretained(this)), - base::Bind(&BlobAsyncBuilderHostTest::CancelCallback, base::Unretained(this))); } - BlobDataBuilder* matching_builder_; + void DecrementBlobRefCount(const std::string& uuid) { + context_.DecrementBlobRefCount(uuid); + } + + bool IsBeingBuiltInContext(const std::string& uuid) { + return context_.IsBeingBuilt(uuid); + } + + content::TestBrowserThreadBundle browser_thread_bundle_; + BlobStorageContext context_; BlobAsyncBuilderHost host_; - bool done_called_; - bool cancel_called_; IPCBlobCreationCancelCode cancel_code_; bool request_called_; std::vector requests_; std::vector memory_handles_; - std::vector file_handles_; + std::set completed_blob_uuid_set_; + + scoped_ptr completed_blob_handle_; }; +// The 'shortcut' method is when the data is included in the initial IPCs and +// the browser uses that instead of requesting the memory. TEST_F(BlobAsyncBuilderHostTest, TestShortcut) { std::vector descriptions; @@ -141,16 +142,37 @@ TEST_F(BlobAsyncBuilderHostTest, TestShortcut) { AddShortcutMemoryItem(5000, &descriptions); BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kBlobType); + expected.set_content_type(kContentType); + expected.set_content_disposition(kContentDisposition); AddShortcutMemoryItem(10, &expected); - expected.AppendBlob(kFakeBlobUUID); + expected.AppendData(kCompletedBlobData); AddShortcutMemoryItem(5000, &expected); - SetMatchingBuilder(&expected); - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); + EXPECT_EQ(BlobTransportResult::DONE, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); + + EXPECT_FALSE(request_called_); + EXPECT_EQ(0u, host_.blob_building_count()); + scoped_ptr handle = context_.GetBlobDataFromUUID(kBlobUUID); + EXPECT_FALSE(handle->IsBeingBuilt()); + EXPECT_FALSE(handle->IsBroken()); + scoped_ptr data = handle->CreateSnapshot(); + EXPECT_EQ(expected, *data); + data.reset(); + handle.reset(); + base::RunLoop().RunUntilIdle(); +}; + +TEST_F(BlobAsyncBuilderHostTest, TestShortcutNoRoom) { + std::vector descriptions; + + AddShortcutMemoryItem(10, &descriptions); + AddBlobItem(&descriptions); + AddShortcutMemoryItem(5000, &descriptions); + + EXPECT_EQ(BlobTransportResult::CANCEL_MEMORY_FULL, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5000)); - EXPECT_TRUE(done_called_); - EXPECT_FALSE(cancel_called_); EXPECT_FALSE(request_called_); EXPECT_EQ(0u, host_.blob_building_count()); }; @@ -160,11 +182,10 @@ TEST_F(BlobAsyncBuilderHostTest, TestSingleSharedMemRequest) { const size_t kSize = kTestBlobStorageIPCThresholdBytes + 1; AddMemoryItem(kSize, &descriptions); - EXPECT_TRUE( - BuildBlobAsync(descriptions, kTestBlobStorageIPCThresholdBytes + 1)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, std::set(), + kTestBlobStorageIPCThresholdBytes + 1)); - EXPECT_FALSE(done_called_); - EXPECT_FALSE(cancel_called_); EXPECT_TRUE(request_called_); EXPECT_EQ(1u, host_.blob_building_count()); ASSERT_EQ(1u, requests_.size()); @@ -183,18 +204,17 @@ TEST_F(BlobAsyncBuilderHostTest, TestMultipleSharedMemRequests) { AddMemoryItem(kSize, &descriptions); BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kBlobType); + expected.set_content_type(kContentType); + expected.set_content_disposition(kContentDisposition); char data[kSize]; memset(data, kFirstBlockByte, kTestBlobStorageMaxSharedMemoryBytes); expected.AppendData(data, kTestBlobStorageMaxSharedMemoryBytes); expected.AppendData(&kSecondBlockByte, 1); - SetMatchingBuilder(&expected); - EXPECT_TRUE( - BuildBlobAsync(descriptions, kTestBlobStorageMaxSharedMemoryBytes + 1)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, std::set(), + kTestBlobStorageMaxSharedMemoryBytes + 1)); - EXPECT_FALSE(done_called_); - EXPECT_FALSE(cancel_called_); EXPECT_TRUE(request_called_); EXPECT_EQ(1u, host_.blob_building_count()); ASSERT_EQ(1u, requests_.size()); @@ -217,10 +237,9 @@ TEST_F(BlobAsyncBuilderHostTest, TestMultipleSharedMemRequests) { BlobItemBytesResponse response(0); std::vector responses = {response}; - EXPECT_TRUE(host_.OnMemoryResponses(kBlobUUID, responses)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); - EXPECT_FALSE(done_called_); - EXPECT_FALSE(cancel_called_); EXPECT_TRUE(request_called_); EXPECT_EQ(1u, host_.blob_building_count()); ASSERT_EQ(1u, requests_.size()); @@ -234,11 +253,16 @@ TEST_F(BlobAsyncBuilderHostTest, TestMultipleSharedMemRequests) { response.request_number = 1; responses[0] = response; - EXPECT_TRUE(host_.OnMemoryResponses(kBlobUUID, responses)); - EXPECT_TRUE(done_called_); - EXPECT_FALSE(cancel_called_); + EXPECT_EQ(BlobTransportResult::DONE, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); EXPECT_FALSE(request_called_); EXPECT_EQ(0u, host_.blob_building_count()); + scoped_ptr blob_handle = + context_.GetBlobDataFromUUID(kBlobUUID); + EXPECT_FALSE(blob_handle->IsBeingBuilt()); + EXPECT_FALSE(blob_handle->IsBroken()); + scoped_ptr blob_data = blob_handle->CreateSnapshot(); + EXPECT_EQ(expected, *blob_data); }; TEST_F(BlobAsyncBuilderHostTest, TestBasicIPCAndStopBuilding) { @@ -249,18 +273,32 @@ TEST_F(BlobAsyncBuilderHostTest, TestBasicIPCAndStopBuilding) { AddMemoryItem(2, &descriptions); BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kBlobType); + expected.set_content_type(kContentType); + expected.set_content_disposition(kContentDisposition); AddShortcutMemoryItem(2, &expected); - expected.AppendBlob(kFakeBlobUUID); + expected.AppendData(kCompletedBlobData); AddShortcutMemoryItem(2, &expected); - SetMatchingBuilder(&expected); - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); - host_.StopBuildingBlob(kBlobUUID); - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); + host_.CancelBuildingBlob(kBlobUUID, IPCBlobCreationCancelCode::UNKNOWN, + &context_); + + // Check that we're broken, and then remove the blob. + scoped_ptr blob_handle = + context_.GetBlobDataFromUUID(kBlobUUID); + EXPECT_FALSE(blob_handle->IsBeingBuilt()); + EXPECT_TRUE(blob_handle->IsBroken()); + blob_handle.reset(); + DecrementBlobRefCount(kBlobUUID); + base::RunLoop().RunUntilIdle(); + blob_handle = context_.GetBlobDataFromUUID(kBlobUUID); + EXPECT_FALSE(blob_handle.get()); + + // This should succeed because we've removed all references to the blob. + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); - EXPECT_FALSE(done_called_); - EXPECT_FALSE(cancel_called_); EXPECT_TRUE(request_called_); EXPECT_EQ(1u, host_.blob_building_count()); request_called_ = false; @@ -270,58 +308,308 @@ TEST_F(BlobAsyncBuilderHostTest, TestBasicIPCAndStopBuilding) { BlobItemBytesResponse response2(1); PopulateBytes(response2.allocate_mutable_data(2), 2); std::vector responses = {response1, response2}; - EXPECT_TRUE(host_.OnMemoryResponses(kBlobUUID, responses)); - EXPECT_TRUE(done_called_); - EXPECT_FALSE(cancel_called_); + + EXPECT_EQ(BlobTransportResult::DONE, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); EXPECT_FALSE(request_called_); EXPECT_EQ(0u, host_.blob_building_count()); + blob_handle = context_.GetBlobDataFromUUID(kBlobUUID); + EXPECT_FALSE(blob_handle->IsBeingBuilt()); + EXPECT_FALSE(blob_handle->IsBroken()); + scoped_ptr blob_data = blob_handle->CreateSnapshot(); + EXPECT_EQ(expected, *blob_data); +}; + +TEST_F(BlobAsyncBuilderHostTest, TestBreakingAllBuilding) { + const std::string& kBlob1 = "blob1"; + const std::string& kBlob2 = "blob2"; + const std::string& kBlob3 = "blob3"; + + // Register blobs. + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob1, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob2, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob3, kContentType, kContentDisposition, + std::set(), &context_)); + + // Start building one of them. + std::vector descriptions; + AddMemoryItem(2, &descriptions); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + host_.StartBuildingBlob( + kBlob1, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_TRUE(request_called_); + + scoped_ptr blob_handle1 = + context_.GetBlobDataFromUUID(kBlob1); + scoped_ptr blob_handle2 = + context_.GetBlobDataFromUUID(kBlob2); + scoped_ptr blob_handle3 = + context_.GetBlobDataFromUUID(kBlob2); + EXPECT_TRUE(blob_handle1->IsBeingBuilt() && blob_handle2->IsBeingBuilt() && + blob_handle3->IsBeingBuilt()); + EXPECT_FALSE(blob_handle1->IsBroken() || blob_handle2->IsBroken() || + blob_handle3->IsBroken()); + + host_.CancelAll(&context_); + + EXPECT_FALSE(blob_handle1->IsBeingBuilt() || blob_handle2->IsBeingBuilt() || + blob_handle3->IsBeingBuilt()); + EXPECT_TRUE(blob_handle1->IsBroken() && blob_handle2->IsBroken() && + blob_handle3->IsBroken()); + blob_handle1.reset(); + blob_handle2.reset(); + blob_handle3.reset(); + base::RunLoop().RunUntilIdle(); }; TEST_F(BlobAsyncBuilderHostTest, TestBadIPCs) { std::vector descriptions; // Test reusing same blob uuid. - SetMatchingBuilder(nullptr); AddMemoryItem(10, &descriptions); AddBlobItem(&descriptions); AddMemoryItem(5000, &descriptions); - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); - EXPECT_FALSE(BuildBlobAsync(descriptions, 5010)); - EXPECT_FALSE(done_called_); - EXPECT_FALSE(cancel_called_); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); EXPECT_FALSE(request_called_); - host_.StopBuildingBlob(kBlobUUID); + host_.CancelBuildingBlob(kBlobUUID, IPCBlobCreationCancelCode::UNKNOWN, + &context_); + base::RunLoop().RunUntilIdle(); + DecrementBlobRefCount(kBlobUUID); + EXPECT_FALSE(context_.GetBlobDataFromUUID(kBlobUUID).get()); - // Test we're _not_ an error if we get a bad uuid for responses. + // Test we're an error if we get a bad uuid for responses. BlobItemBytesResponse response(0); std::vector responses = {response}; - EXPECT_TRUE(host_.OnMemoryResponses(kBlobUUID, responses)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); // Test empty responses. responses.clear(); - EXPECT_FALSE(host_.OnMemoryResponses(kBlobUUID, responses)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); // Test response problems below here. descriptions.clear(); AddMemoryItem(2, &descriptions); AddBlobItem(&descriptions); AddMemoryItem(2, &descriptions); - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); // Invalid request number. BlobItemBytesResponse response1(3); PopulateBytes(response1.allocate_mutable_data(2), 2); responses = {response1}; - EXPECT_FALSE(host_.OnMemoryResponses(kBlobUUID, responses)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); + DecrementBlobRefCount(kBlobUUID); + base::RunLoop().RunUntilIdle(); // Duplicate request number responses. - EXPECT_TRUE(BuildBlobAsync(descriptions, 5010)); + EXPECT_EQ(BlobTransportResult::PENDING_RESPONSES, + BuildBlobAsync(descriptions, completed_blob_uuid_set_, 5010)); response1.request_number = 0; BlobItemBytesResponse response2(0); PopulateBytes(response2.allocate_mutable_data(2), 2); responses = {response1, response2}; - EXPECT_FALSE(host_.OnMemoryResponses(kBlobUUID, responses)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.OnMemoryResponses(kBlobUUID, responses, &context_)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); + DecrementBlobRefCount(kBlobUUID); + base::RunLoop().RunUntilIdle(); +}; + +TEST_F(BlobAsyncBuilderHostTest, WaitOnReferencedBlob) { + const std::string& kBlob1 = "blob1"; + const std::string& kBlob2 = "blob2"; + const std::string& kBlob3 = "blob3"; + + // Register blobs. + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob1, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob2, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob3, kContentType, kContentDisposition, + {kBlob1, kBlob2}, &context_)); + + // Finish the third one, with a reference to the first and second blob. + std::vector descriptions; + AddShortcutMemoryItem(2, &descriptions); + DataElement element; + element.SetToBlob(kBlob1); + descriptions.push_back(element); + element.SetToBlob(kBlob2); + descriptions.push_back(element); + + // Finish the third, but we should still be 'building' it. + EXPECT_EQ(BlobTransportResult::DONE, + host_.StartBuildingBlob( + kBlob3, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(request_called_); + EXPECT_TRUE(host_.IsBeingBuilt(kBlob3)); + EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); + + // Finish the first. + descriptions.clear(); + AddShortcutMemoryItem(2, &descriptions); + EXPECT_EQ(BlobTransportResult::DONE, + host_.StartBuildingBlob( + kBlob1, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(request_called_); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob1)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob1)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob1)); + + // Run the message loop so we propogate the construction complete callbacks. + base::RunLoop().RunUntilIdle(); + // Verify we're not done. + EXPECT_TRUE(host_.IsBeingBuilt(kBlob3)); + EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); + + // Finish the second. + EXPECT_EQ(BlobTransportResult::DONE, + host_.StartBuildingBlob( + kBlob2, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(request_called_); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob2)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob2)); + + // Run the message loop so we propogate the construction complete callbacks. + base::RunLoop().RunUntilIdle(); + // Finally, we should be finished with third blob. + EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob3)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob3)); +}; + +TEST_F(BlobAsyncBuilderHostTest, IncorrectBlobDependencies) { + const std::string& kGoodBlob = "goodBlob"; + const std::string& kBlob1 = "blob1"; + const std::string& kBlob2 = "blob2"; + const std::string& kBlob3 = "blob3"; + + // Register blobs. Blob 1 has a reference to itself, Blob 2 has a reference + // but doesn't use it, and blob 3 doesn't list it's reference. + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kGoodBlob, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.RegisterBlobUUID(kBlob1, kContentType, kContentDisposition, + {kBlob1}, &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob2, kContentType, kContentDisposition, + {kGoodBlob}, &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob3, kContentType, kContentDisposition, + std::set(), &context_)); + + // The first blob shouldn't be building anymore. + EXPECT_FALSE(host_.IsBeingBuilt(kBlob1)); + + // Try to finish the second one, without a reference to the first. + std::vector descriptions; + AddShortcutMemoryItem(2, &descriptions); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.StartBuildingBlob( + kBlob2, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); + + // Try to finish the third one with the reference we didn't declare earlier. + descriptions.clear(); + AddShortcutMemoryItem(2, &descriptions); + DataElement element; + element.SetToBlob(kGoodBlob); + descriptions.push_back(element); + EXPECT_EQ(BlobTransportResult::BAD_IPC, + host_.StartBuildingBlob( + kBlob3, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); +}; + +TEST_F(BlobAsyncBuilderHostTest, BlobBreaksWhenReferenceBreaks) { + const std::string& kBlob1 = "blob1"; + const std::string& kBlob2 = "blob2"; + + // Register blobs. + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob1, kContentType, kContentDisposition, + std::set(), &context_)); + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob2, kContentType, kContentDisposition, + {kBlob1}, &context_)); + + // Finish the second one, with a reference to the first. + std::vector descriptions; + AddShortcutMemoryItem(2, &descriptions); + DataElement element; + element.SetToBlob(kBlob1); + descriptions.push_back(element); + EXPECT_EQ(BlobTransportResult::DONE, + host_.StartBuildingBlob( + kBlob2, descriptions, 2, &context_, + base::Bind(&BlobAsyncBuilderHostTest::RequestMemoryCallback, + base::Unretained(this)))); + EXPECT_FALSE(request_called_); + EXPECT_TRUE(host_.IsBeingBuilt(kBlob2)); + EXPECT_TRUE(IsBeingBuiltInContext(kBlob2)); + + // Break the first. + descriptions.clear(); + host_.CancelBuildingBlob(kBlob1, IPCBlobCreationCancelCode::UNKNOWN, + &context_); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob1)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob1)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob1)->IsBroken()); + + // Run the message loop so we propogate the construction complete callbacks. + base::RunLoop().RunUntilIdle(); + // We should be finished with third blob, and it should be broken. + EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob2)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob2)->IsBroken()); +}; + +TEST_F(BlobAsyncBuilderHostTest, BlobBreaksWhenReferenceBroken) { + const std::string& kBlob1 = "blob1"; + const std::string& kBlob2 = "blob2"; + + // Register blobs. + EXPECT_EQ(BlobTransportResult::DONE, + host_.RegisterBlobUUID(kBlob1, kContentType, kContentDisposition, + std::set(), &context_)); + host_.CancelBuildingBlob(kBlob1, IPCBlobCreationCancelCode::UNKNOWN, + &context_); + EXPECT_EQ(BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN, + host_.RegisterBlobUUID(kBlob2, kContentType, kContentDisposition, + {kBlob1}, &context_)); + EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); + EXPECT_FALSE(IsBeingBuiltInContext(kBlob2)); + EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob2)->IsBroken()); }; -} // namespace } // namespace storage diff --git a/chromium/content/browser/blob_storage/blob_async_transport_request_builder_unittest.cc b/chromium/content/browser/blob_storage/blob_async_transport_request_builder_unittest.cc new file mode 100644 index 00000000000..071a06bb23f --- /dev/null +++ b/chromium/content/browser/blob_storage/blob_async_transport_request_builder_unittest.cc @@ -0,0 +1,356 @@ +// 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 +#include + +#include + +#include "base/logging.h" +#include "storage/browser/blob/blob_async_transport_request_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace storage { +namespace { + +const char kNewUUID[] = "newUUID"; +const base::FilePath kFuturePopulatingFilePath = base::FilePath::FromUTF8Unsafe( + std::string(BlobDataBuilder::kAppendFutureFileTemporaryFileName)); +const char kFakeBlobUUID[] = "fakeBlob"; + +void AddMemoryItem(size_t length, std::vector* out) { + DataElement bytes; + bytes.SetToBytesDescription(length); + out->push_back(bytes); +} + +void AddShortcutMemoryItem(size_t length, std::vector* out) { + DataElement bytes; + bytes.SetToAllocatedBytes(length); + for (size_t i = 0; i < length; i++) { + bytes.mutable_bytes()[i] = static_cast(i); + } + out->push_back(bytes); +} + +void AddBlobItem(std::vector* out) { + DataElement blob; + blob.SetToBlob(kFakeBlobUUID); + out->push_back(blob); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestNoMemoryItems) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + // Here we test that we don't do any requests when there are no memory items. + AddBlobItem(&infos); + AddBlobItem(&infos); + AddBlobItem(&infos); + strategy.InitializeForIPCRequests(100, // max_ipc_memory_size + 0, // blob_total_size + infos, &builder); + + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(0u, strategy.file_sizes().size()); + EXPECT_EQ(0u, strategy.requests().size()); + + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendBlob(kFakeBlobUUID); + expected_builder.AppendBlob(kFakeBlobUUID); + expected_builder.AppendBlob(kFakeBlobUUID); + EXPECT_EQ(expected_builder, builder); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestLargeBlockToFile) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + AddMemoryItem(305, &infos); + strategy.InitializeForFileRequests(400, // max_file_size + 305, // blob_total_size + infos, &builder); + + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(1u, strategy.file_sizes().size()); + EXPECT_EQ(305ul, strategy.file_sizes().at(0)); + EXPECT_EQ(1u, strategy.requests().size()); + + auto& memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 305ull, 0u, 0ull), + memory_item_request.message); + + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendFile(kFuturePopulatingFilePath, 0, 305, + base::Time::FromDoubleT(0)); + EXPECT_EQ(expected_builder, builder); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestLargeBlockToFiles) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + AddMemoryItem(1000, &infos); + strategy.InitializeForFileRequests(400, // max_file_size + 1000, // blob_total_size + infos, &builder); + + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(3u, strategy.file_sizes().size()); + EXPECT_EQ(400ul, strategy.file_sizes().at(0)); + EXPECT_EQ(400ul, strategy.file_sizes().at(1)); + EXPECT_EQ(200ul, strategy.file_sizes().at(2)); + EXPECT_EQ(3u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 400ull, 0u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(1); + EXPECT_EQ(1u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(1u, 0u, 400ull, 400ull, 1u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(2); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(2u, 0u, 800ull, 200ull, 2u, 0ull), + memory_item_request.message); + + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendFile(kFuturePopulatingFilePath, 0, 400, + base::Time::FromDoubleT(0)); + expected_builder.AppendFile(kFuturePopulatingFilePath, 0, 400, + base::Time::FromDoubleT(0)); + expected_builder.AppendFile(kFuturePopulatingFilePath, 0, 200, + base::Time::FromDoubleT(0)); + EXPECT_EQ(expected_builder, builder); +} + +TEST(BlobAsyncTransportRequestBuilderTest, + TestLargeBlocksConsolidatingInFiles) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + // We should have 3 storage items for the memory, two files, 400 each. + // We end up with storage items: + // 1: File A, 300MB + // 2: Blob + // 3: File A, 100MB (300MB offset) + // 4: File B, 400MB + AddMemoryItem(300, &infos); + AddBlobItem(&infos); + AddMemoryItem(500, &infos); + + strategy.InitializeForFileRequests(400, // max_file_size + 800, // blob_total_size + infos, &builder); + + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(2u, strategy.file_sizes().size()); + EXPECT_EQ(400ul, strategy.file_sizes().at(0)); + EXPECT_EQ(400ul, strategy.file_sizes().at(1)); + EXPECT_EQ(3u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 300ull, 0u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(1); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(1u, 2u, 0ull, 100ull, 0u, 300ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(2); + EXPECT_EQ(3u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ( + BlobItemBytesRequest::CreateFileRequest(2u, 2u, 100ull, 400ull, 1u, 0ull), + memory_item_request.message); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestSharedMemorySegmentation) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + // For transport we should have 3 shared memories, and then storage in 3 + // browser items. + AddMemoryItem(500, &infos); + strategy.InitializeForSharedMemoryRequests(200, // max_shared_memory_size + 500, // total_blob_size + infos, &builder); + + EXPECT_EQ(0u, strategy.file_sizes().size()); + EXPECT_EQ(3u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(200u, strategy.shared_memory_sizes().at(0)); + EXPECT_EQ(200u, strategy.shared_memory_sizes().at(1)); + EXPECT_EQ(100u, strategy.shared_memory_sizes().at(2)); + EXPECT_EQ(3u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(0u, 0u, 0ull, + 200ull, 0u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(1); + EXPECT_EQ(1u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(1u, 0u, 200ull, + 200ull, 1u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(2); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(2u, 0u, 400ull, + 100ull, 2u, 0ull), + memory_item_request.message); + + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendFutureData(200); + expected_builder.AppendFutureData(200); + expected_builder.AppendFutureData(100); + EXPECT_EQ(expected_builder, builder); +} + +TEST(BlobAsyncTransportRequestBuilderTest, + TestSharedMemorySegmentationAndStorage) { + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + std::vector infos; + + // For transport, we should have 2 shared memories, where the first one + // have half 0 and half 3, and then the last one has half 3. + // + // For storage, we should have 3 browser items that match the pre-transport + // version: + // 1: Bytes 100MB + // 2: Blob + // 3: Bytes 200MB + AddShortcutMemoryItem(100, &infos); // should have no behavior change + AddBlobItem(&infos); + AddMemoryItem(200, &infos); + + strategy.InitializeForSharedMemoryRequests(200, // max_shared_memory_size + 300, // total_blob_size + infos, &builder); + + EXPECT_EQ(0u, strategy.file_sizes().size()); + EXPECT_EQ(2u, strategy.shared_memory_sizes().size()); + EXPECT_EQ(200u, strategy.shared_memory_sizes().at(0)); + EXPECT_EQ(100u, strategy.shared_memory_sizes().at(1)); + EXPECT_EQ(3u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(0u, 0u, 0ull, + 100ull, 0u, 0ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(1); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(1u, 2u, 0ull, + 100ull, 0u, 100ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(2); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(100u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(2u, 2u, 100ull, + 100ull, 1u, 0ull), + memory_item_request.message); + + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendFutureData(100); + expected_builder.AppendBlob(kFakeBlobUUID); + expected_builder.AppendFutureData(200); + EXPECT_EQ(expected_builder, builder); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestSimpleIPC) { + // Test simple IPC strategy, where size < max_ipc_memory_size and we have + // just one item. + std::vector infos; + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + AddMemoryItem(10, &infos); + AddBlobItem(&infos); + + strategy.InitializeForIPCRequests(100, // max_ipc_memory_size + 10, // total_blob_size + infos, &builder); + + EXPECT_EQ(0u, strategy.file_sizes().size()); + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + ASSERT_EQ(1u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(0u, 0u, 0ull, 10ull), + memory_item_request.message); +} + +TEST(BlobAsyncTransportRequestBuilderTest, TestMultipleIPC) { + // Same as above, but with 2 items and a blob in-between. + std::vector infos; + BlobAsyncTransportRequestBuilder strategy; + BlobDataBuilder builder(kNewUUID); + AddShortcutMemoryItem(10, &infos); // should have no behavior change + AddBlobItem(&infos); + AddMemoryItem(80, &infos); + + strategy.InitializeForIPCRequests(100, // max_ipc_memory_size + 90, // total_blob_size + infos, &builder); + + EXPECT_EQ(0u, strategy.file_sizes().size()); + EXPECT_EQ(0u, strategy.shared_memory_sizes().size()); + ASSERT_EQ(2u, strategy.requests().size()); + + auto memory_item_request = strategy.requests().at(0); + EXPECT_EQ(0u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(0u, 0u, 0ull, 10ull), + memory_item_request.message); + + memory_item_request = strategy.requests().at(1); + EXPECT_EQ(2u, memory_item_request.browser_item_index); + EXPECT_EQ(0u, memory_item_request.browser_item_offset); + EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(1u, 2u, 0ull, 80ull), + memory_item_request.message); + + // We still populate future data, as the strategy assumes we will be + // requesting the data. + BlobDataBuilder expected_builder(kNewUUID); + expected_builder.AppendFutureData(10); + expected_builder.AppendBlob(kFakeBlobUUID); + expected_builder.AppendFutureData(80); + EXPECT_EQ(expected_builder, builder); +} +} // namespace +} // namespace storage diff --git a/chromium/content/browser/blob_storage/blob_async_transport_strategy_unittest.cc b/chromium/content/browser/blob_storage/blob_async_transport_strategy_unittest.cc deleted file mode 100644 index 6318439e916..00000000000 --- a/chromium/content/browser/blob_storage/blob_async_transport_strategy_unittest.cc +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "storage/browser/blob/blob_async_transport_strategy.h" - -#include -#include - -#include - -#include "base/logging.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace storage { -namespace { - -const char kNewUUID[] = "newUUID"; -const base::FilePath kFuturePopulatingFilePath = base::FilePath::FromUTF8Unsafe( - std::string(BlobDataBuilder::kAppendFutureFileTemporaryFileName)); -const char kFakeBlobUUID[] = "fakeBlob"; - -void AddMemoryItem(size_t length, std::vector* out) { - DataElement bytes; - bytes.SetToBytesDescription(length); - out->push_back(bytes); -} - -void AddShortcutMemoryItem(size_t length, std::vector* out) { - DataElement bytes; - bytes.SetToAllocatedBytes(length); - for (size_t i = 0; i < length; i++) { - bytes.mutable_bytes()[i] = static_cast(i); - } - out->push_back(bytes); -} - -void AddBlobItem(std::vector* out) { - DataElement blob; - blob.SetToBlob(kFakeBlobUUID); - out->push_back(blob); -} - -TEST(BlobAsyncTransportStrategyTest, TestNoMemoryItems) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // Here we test that we don't do any requests when there are no memory items. - AddBlobItem(&infos); - AddBlobItem(&infos); - AddBlobItem(&infos); - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_available - kNewUUID, infos); - - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(0u, strategy.handle_sizes().size()); - EXPECT_EQ(0u, strategy.requests().size()); - - BlobDataBuilder builder(kNewUUID); - builder.AppendBlob(kFakeBlobUUID); - builder.AppendBlob(kFakeBlobUUID); - builder.AppendBlob(kFakeBlobUUID); - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, TestLargeBlockToFile) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // Here we test our size > max_blob_in_memory_size (100), - // and we save to one file. (size < max_file_size) - AddMemoryItem(305, &infos); - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_available - kNewUUID, infos); - - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(1u, strategy.handle_sizes().size()); - EXPECT_EQ(305ul, strategy.handle_sizes().at(0)); - EXPECT_EQ(1u, strategy.requests().size()); - - auto& memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 305ull, 0u, 0ull), - memory_item_request.message); - - BlobDataBuilder builder(kNewUUID); - builder.AppendFile(kFuturePopulatingFilePath, 0, 305, - base::Time::FromDoubleT(0)); - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, TestLargeBlockToFiles) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // Here we test our size > max_blob_in_memory_size (300), - // and we save 3 files. (size > max_file_size) - AddMemoryItem(1000, &infos); - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_available - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(3u, strategy.handle_sizes().size()); - EXPECT_EQ(400ul, strategy.handle_sizes().at(0)); - EXPECT_EQ(400ul, strategy.handle_sizes().at(1)); - EXPECT_EQ(200ul, strategy.handle_sizes().at(2)); - EXPECT_EQ(3u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 400ull, 0u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(1); - EXPECT_EQ(1u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(1u, 0u, 400ull, 400ull, 1u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(2); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(2u, 0u, 800ull, 200ull, 2u, 0ull), - memory_item_request.message); - - BlobDataBuilder builder(kNewUUID); - builder.AppendFile(kFuturePopulatingFilePath, 0, 400, - base::Time::FromDoubleT(0)); - builder.AppendFile(kFuturePopulatingFilePath, 0, 400, - base::Time::FromDoubleT(0)); - builder.AppendFile(kFuturePopulatingFilePath, 0, 200, - base::Time::FromDoubleT(0)); - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, TestLargeBlocksConsolidatingInFiles) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // We should have 3 storage items for the memory, two files, 400 each. - // We end up with storage items: - // 1: File A, 300MB - // 2: Blob - // 3: File A, 100MB (300MB offset) - // 4: File B, 400MB - AddMemoryItem(300, &infos); - AddBlobItem(&infos); - AddMemoryItem(500, &infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_available - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(2u, strategy.handle_sizes().size()); - EXPECT_EQ(400ul, strategy.handle_sizes().at(0)); - EXPECT_EQ(400ul, strategy.handle_sizes().at(1)); - EXPECT_EQ(3u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(0u, 0u, 0ull, 300ull, 0u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(1); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(1u, 2u, 0ull, 100ull, 0u, 300ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(2); - EXPECT_EQ(3u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ( - BlobItemBytesRequest::CreateFileRequest(2u, 2u, 100ull, 400ull, 1u, 0ull), - memory_item_request.message); -} - -TEST(BlobAsyncTransportStrategyTest, TestSharedMemorySegmentation) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // For transport we should have 3 shared memories, and then storage in 3 - // browser items. - // (size > max_shared_memory_size and size < max_blob_in_memory_size - AddMemoryItem(500, &infos); - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 300, // max_file_size - 5000, // disk_space_left - 500, // memory_available - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(3u, strategy.handle_sizes().size()); - EXPECT_EQ(200u, strategy.handle_sizes().at(0)); - EXPECT_EQ(200u, strategy.handle_sizes().at(1)); - EXPECT_EQ(100u, strategy.handle_sizes().at(2)); - EXPECT_EQ(3u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(0u, 0u, 0ull, - 200ull, 0u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(1); - EXPECT_EQ(1u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(1u, 0u, 200ull, - 200ull, 1u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(2); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(2u, 0u, 400ull, - 100ull, 2u, 0ull), - memory_item_request.message); - - BlobDataBuilder builder(kNewUUID); - builder.AppendFutureData(200); - builder.AppendFutureData(200); - builder.AppendFutureData(100); - - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, TestSharedMemorySegmentationAndStorage) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // For transport, we should have 2 shared memories, where the first one - // have half 0 and half 3, and then the last one has half 3. - // - // For storage, we should have 3 browser items that match the pre-transport - // version: - // 1: Bytes 100MB - // 2: Blob - // 3: Bytes 200MB - AddShortcutMemoryItem(100, &infos); // should have no behavior change - AddBlobItem(&infos); - AddMemoryItem(200, &infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 300, // max_file_size - 5000, // disk_space_left - 300, // memory_available - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - EXPECT_EQ(2u, strategy.handle_sizes().size()); - EXPECT_EQ(200u, strategy.handle_sizes().at(0)); - EXPECT_EQ(100u, strategy.handle_sizes().at(1)); - EXPECT_EQ(3u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(0u, 0u, 0ull, - 100ull, 0u, 0ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(1); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(1u, 2u, 0ull, - 100ull, 0u, 100ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(2); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(100u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateSharedMemoryRequest(2u, 2u, 100ull, - 100ull, 1u, 0ull), - memory_item_request.message); - - BlobDataBuilder builder(kNewUUID); - builder.AppendFutureData(100); - builder.AppendBlob(kFakeBlobUUID); - builder.AppendFutureData(200); - - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, TestTooLarge) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // Our item is too large for disk, so error out. - AddMemoryItem(5001, &infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_left - kNewUUID, infos); - - EXPECT_EQ(0u, strategy.handle_sizes().size()); - EXPECT_EQ(0u, strategy.handle_sizes().size()); - EXPECT_EQ(0u, strategy.requests().size()); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_TOO_LARGE, strategy.error()); -} - -TEST(BlobAsyncTransportStrategyTest, TestNoDisk) { - BlobAsyncTransportStrategy strategy; - std::vector infos; - - // Our item is too large for memory, and we are in no_disk mode (incognito) - AddMemoryItem(301, &infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 0, // disk_space_left - 300, // memory_available - kNewUUID, infos); - - EXPECT_EQ(0u, strategy.handle_sizes().size()); - EXPECT_EQ(0u, strategy.handle_sizes().size()); - EXPECT_EQ(0u, strategy.requests().size()); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_TOO_LARGE, strategy.error()); -} - -TEST(BlobAsyncTransportStrategyTest, TestSimpleIPC) { - // Test simple IPC strategy, where size < max_ipc_memory_size and we have - // just one item. - std::vector infos; - BlobAsyncTransportStrategy strategy; - AddMemoryItem(10, &infos); - AddBlobItem(&infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_left - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - ASSERT_EQ(1u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(0u, 0u, 0ull, 10ull), - memory_item_request.message); -} - -TEST(BlobAsyncTransportStrategyTest, TestMultipleIPC) { - // Same as above, but with 2 items and a blob in-between. - std::vector infos; - BlobAsyncTransportStrategy strategy; - infos.clear(); - AddShortcutMemoryItem(10, &infos); // should have no behavior change - AddBlobItem(&infos); - AddMemoryItem(80, &infos); - - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_left - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_NONE, strategy.error()); - - ASSERT_EQ(2u, strategy.requests().size()); - - auto memory_item_request = strategy.requests().at(0); - EXPECT_EQ(0u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(0u, 0u, 0ull, 10ull), - memory_item_request.message); - - memory_item_request = strategy.requests().at(1); - EXPECT_EQ(2u, memory_item_request.browser_item_index); - EXPECT_EQ(0u, memory_item_request.browser_item_offset); - EXPECT_EQ(BlobItemBytesRequest::CreateIPCRequest(1u, 2u, 0ull, 80ull), - memory_item_request.message); - - // We still populate future data, as the strategy assumes we will be - // requesting the data. - BlobDataBuilder builder(kNewUUID); - builder.AppendFutureData(10); - builder.AppendBlob(kFakeBlobUUID); - builder.AppendFutureData(80); - - EXPECT_EQ(builder, *strategy.blob_builder()); -} - -TEST(BlobAsyncTransportStrategyTest, Shortcut) { - std::vector infos; - AddMemoryItem(100, &infos); - AddBlobItem(&infos); - EXPECT_FALSE(BlobAsyncTransportStrategy::ShouldBeShortcut(infos, 200)); - - infos.clear(); - AddShortcutMemoryItem(100, &infos); - AddBlobItem(&infos); - EXPECT_TRUE(BlobAsyncTransportStrategy::ShouldBeShortcut(infos, 200)); - - infos.clear(); - AddShortcutMemoryItem(100, &infos); - EXPECT_FALSE(BlobAsyncTransportStrategy::ShouldBeShortcut(infos, 99)); -} -} // namespace - -TEST(BlobAsyncTransportStrategyTest, TestInvalidParams) { - std::vector infos; - // In order to test uin64_t overflow, we would need to have an array with more - // than size_t entries (for 32 byte stuff). So this would only happen if the - // IPC was malformed. We instead have to friend this test from DataElement so - // we can modify the length to be > size_t. - - // Test uint64_t overflow. - BlobAsyncTransportStrategy strategy; - AddMemoryItem(1, &infos); - AddMemoryItem(1, &infos); - infos.back().length_ = std::numeric_limits::max(); - strategy.Initialize(100, // max_ipc_memory_size - 200, // max_shared_memory_size - 400, // max_file_size - 5000, // disk_space_left - 100, // memory_left - kNewUUID, infos); - EXPECT_EQ(BlobAsyncTransportStrategy::ERROR_INVALID_PARAMS, - strategy.error()); -} -} // namespace storage diff --git a/chromium/content/browser/blob_storage/blob_dispatcher_host.cc b/chromium/content/browser/blob_storage/blob_dispatcher_host.cc new file mode 100644 index 00000000000..d61653cdaf5 --- /dev/null +++ b/chromium/content/browser/blob_storage/blob_dispatcher_host.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 "content/browser/blob_storage/blob_dispatcher_host.h" + +#include + +#include "base/bind.h" +#include "base/metrics/histogram_macros.h" +#include "content/browser/bad_message.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" +#include "content/common/fileapi/webblob_messages.h" +#include "ipc/ipc_platform_file.h" +#include "storage/browser/blob/blob_storage_context.h" +#include "storage/browser/blob/blob_transport_result.h" +#include "storage/common/blob_storage/blob_item_bytes_request.h" +#include "storage/common/blob_storage/blob_item_bytes_response.h" +#include "storage/common/data_element.h" +#include "url/gurl.h" + +using storage::BlobStorageContext; +using storage::BlobStorageRegistry; +using storage::BlobTransportResult; +using storage::IPCBlobCreationCancelCode; + +namespace content { +namespace { + +// These are used for UMA stats, don't change. +enum RefcountOperation { + BDH_DECREMENT = 0, + BDH_INCREMENT, + BDH_TRACING_ENUM_LAST +}; + +} // namespace + +BlobDispatcherHost::BlobDispatcherHost( + ChromeBlobStorageContext* blob_storage_context) + : BrowserMessageFilter(BlobMsgStart), + blob_storage_context_(blob_storage_context) {} + +BlobDispatcherHost::~BlobDispatcherHost() { + ClearHostFromBlobStorageContext(); +} + +void BlobDispatcherHost::OnChannelClosing() { + ClearHostFromBlobStorageContext(); + public_blob_urls_.clear(); + blobs_inuse_map_.clear(); +} + +bool BlobDispatcherHost::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(BlobDispatcherHost, message) + IPC_MESSAGE_HANDLER(BlobStorageMsg_RegisterBlobUUID, OnRegisterBlobUUID) + IPC_MESSAGE_HANDLER(BlobStorageMsg_StartBuildingBlob, OnStartBuildingBlob) + IPC_MESSAGE_HANDLER(BlobStorageMsg_MemoryItemResponse, OnMemoryItemResponse) + IPC_MESSAGE_HANDLER(BlobStorageMsg_CancelBuildingBlob, OnCancelBuildingBlob) + IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, OnIncrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, OnDecrementBlobRefCount) + IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, OnRegisterPublicBlobURL) + IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void BlobDispatcherHost::OnRegisterBlobUUID( + const std::string& uuid, + const std::string& content_type, + const std::string& content_disposition, + const std::set& referenced_blob_uuids) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + BlobStorageContext* context = this->context(); + if (uuid.empty() || context->registry().HasEntry(uuid) || + async_builder_.IsBeingBuilt(uuid)) { + bad_message::ReceivedBadMessage(this, bad_message::BDH_UUID_REGISTERED); + return; + } + blobs_inuse_map_[uuid] = 1; + BlobTransportResult result = async_builder_.RegisterBlobUUID( + uuid, content_type, content_disposition, referenced_blob_uuids, context); + switch (result) { + case BlobTransportResult::BAD_IPC: + blobs_inuse_map_.erase(uuid); + bad_message::ReceivedBadMessage(this, + bad_message::BDH_CONSTRUCTION_FAILED); + break; + case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: + // The async builder builds the blob as broken, and we just need to send + // the cancel message back to the renderer. + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN)); + break; + case BlobTransportResult::DONE: + break; + case BlobTransportResult::CANCEL_MEMORY_FULL: + case BlobTransportResult::CANCEL_FILE_ERROR: + case BlobTransportResult::CANCEL_UNKNOWN: + case BlobTransportResult::PENDING_RESPONSES: + NOTREACHED(); + break; + } +} + +void BlobDispatcherHost::OnStartBuildingBlob( + const std::string& uuid, + const std::vector& descriptions) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (uuid.empty()) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + BlobStorageContext* context = this->context(); + const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); + if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { + // We ignore messages for blobs that don't exist to handle the case where + // the renderer de-refs a blob that we're still constructing, and there are + // no references to that blob. We ignore broken as well, in the case where + // we decided to break a blob after RegisterBlobUUID is called. + // Second, if the last dereference of the blob happened on a different host, + // then we still haven't gotten rid of the 'building' state in the original + // host. So we call cancel, and send the message just in case that happens. + if (async_builder_.IsBeingBuilt(uuid)) { + async_builder_.CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, + context); + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); + } + return; + } + if (!async_builder_.IsBeingBuilt(uuid)) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + // |this| owns async_builder_ so using base::Unretained(this) is safe. + BlobTransportResult result = async_builder_.StartBuildingBlob( + uuid, descriptions, context->memory_available(), context, + base::Bind(&BlobDispatcherHost::SendMemoryRequest, base::Unretained(this), + uuid)); + SendIPCResponse(uuid, result); +} + +void BlobDispatcherHost::OnMemoryItemResponse( + const std::string& uuid, + const std::vector& responses) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (uuid.empty()) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + BlobStorageContext* context = this->context(); + const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); + if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { + // We ignore messages for blobs that don't exist to handle the case where + // the renderer de-refs a blob that we're still constructing, and there are + // no references to that blob. We ignore broken as well, in the case where + // we decided to break a blob after sending the memory request. + // Note: if a blob is broken, then it can't be in the async_builder. + // Second, if the last dereference of the blob happened on a different host, + // then we still haven't gotten rid of the 'building' state in the original + // host. So we call cancel, and send the message just in case that happens. + if (async_builder_.IsBeingBuilt(uuid)) { + async_builder_.CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, + context); + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); + } + return; + } + if (!async_builder_.IsBeingBuilt(uuid)) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + BlobTransportResult result = + async_builder_.OnMemoryResponses(uuid, responses, context); + SendIPCResponse(uuid, result); +} + +void BlobDispatcherHost::OnCancelBuildingBlob( + const std::string& uuid, + const storage::IPCBlobCreationCancelCode code) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (uuid.empty()) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + BlobStorageContext* context = this->context(); + const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); + if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { + // We ignore messages for blobs that don't exist to handle the case where + // the renderer de-refs a blob that we're still constructing, and there are + // no references to that blob. We ignore broken as well, in the case where + // we decided to break a blob and the renderer also decided to cancel. + // Note: if a blob is broken, then it can't be in the async_builder. + // Second, if the last dereference of the blob happened on a different host, + // then we still haven't gotten rid of the 'building' state in the original + // host. So we call cancel just in case this happens. + if (async_builder_.IsBeingBuilt(uuid)) { + async_builder_.CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, + context); + } + return; + } + if (!async_builder_.IsBeingBuilt(uuid)) { + SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); + return; + } + VLOG(1) << "Blob construction of " << uuid << " cancelled by renderer. " + << " Reason: " << static_cast(code) << "."; + async_builder_.CancelBuildingBlob(uuid, code, context); +} + +void BlobDispatcherHost::OnIncrementBlobRefCount(const std::string& uuid) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + BlobStorageContext* context = this->context(); + if (uuid.empty()) { + bad_message::ReceivedBadMessage( + this, bad_message::BDH_INVALID_REFCOUNT_OPERATION); + return; + } + if (!context->registry().HasEntry(uuid)) { + UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidReference", BDH_INCREMENT, + BDH_TRACING_ENUM_LAST); + return; + } + context->IncrementBlobRefCount(uuid); + blobs_inuse_map_[uuid] += 1; +} + +void BlobDispatcherHost::OnDecrementBlobRefCount(const std::string& uuid) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (uuid.empty()) { + bad_message::ReceivedBadMessage( + this, bad_message::BDH_INVALID_REFCOUNT_OPERATION); + return; + } + if (!IsInUseInHost(uuid)) { + UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidReference", BDH_DECREMENT, + BDH_TRACING_ENUM_LAST); + return; + } + BlobStorageContext* context = this->context(); + context->DecrementBlobRefCount(uuid); + blobs_inuse_map_[uuid] -= 1; + if (blobs_inuse_map_[uuid] == 0) { + blobs_inuse_map_.erase(uuid); + // If the blob has been deleted in the context and we're still building it, + // this means we have no references waiting to read it. Clear the building + // state and send a cancel message to the renderer. + if (async_builder_.IsBeingBuilt(uuid) && + !context->registry().HasEntry(uuid)) { + async_builder_.CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, + context); + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); + } + } +} + +void BlobDispatcherHost::OnRegisterPublicBlobURL(const GURL& public_url, + const std::string& uuid) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + BlobStorageContext* context = this->context(); + if (uuid.empty()) { + bad_message::ReceivedBadMessage(this, + bad_message::BDH_INVALID_URL_OPERATION); + return; + } + if (!IsInUseInHost(uuid) || context->registry().IsURLMapped(public_url)) { + UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidURLRegister", BDH_INCREMENT, + BDH_TRACING_ENUM_LAST); + return; + } + context->RegisterPublicBlobURL(public_url, uuid); + public_blob_urls_.insert(public_url); +} + +void BlobDispatcherHost::OnRevokePublicBlobURL(const GURL& public_url) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!public_url.is_valid()) { + bad_message::ReceivedBadMessage(this, + bad_message::BDH_INVALID_URL_OPERATION); + return; + } + if (!IsUrlRegisteredInHost(public_url)) { + UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidURLRegister", BDH_DECREMENT, + BDH_TRACING_ENUM_LAST); + return; + } + context()->RevokePublicBlobURL(public_url); + public_blob_urls_.erase(public_url); +} + +storage::BlobStorageContext* BlobDispatcherHost::context() { + return blob_storage_context_->context(); +} + +void BlobDispatcherHost::SendMemoryRequest( + const std::string& uuid, + scoped_ptr> requests, + scoped_ptr> memory_handles, + scoped_ptr> files) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + std::vector file_handles; + // TODO(dmurph): Support file-backed blob transportation. + DCHECK(files->empty()); + Send(new BlobStorageMsg_RequestMemoryItem(uuid, *requests, *memory_handles, + file_handles)); +} + +void BlobDispatcherHost::SendIPCResponse(const std::string& uuid, + storage::BlobTransportResult result) { + switch (result) { + case BlobTransportResult::BAD_IPC: + bad_message::ReceivedBadMessage(this, + bad_message::BDH_CONSTRUCTION_FAILED); + return; + case BlobTransportResult::CANCEL_MEMORY_FULL: + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY)); + return; + case BlobTransportResult::CANCEL_FILE_ERROR: + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::FILE_WRITE_FAILED)); + return; + case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN)); + return; + case BlobTransportResult::CANCEL_UNKNOWN: + Send(new BlobStorageMsg_CancelBuildingBlob( + uuid, IPCBlobCreationCancelCode::UNKNOWN)); + return; + case BlobTransportResult::PENDING_RESPONSES: + return; + case BlobTransportResult::DONE: + Send(new BlobStorageMsg_DoneBuildingBlob(uuid)); + return; + } + NOTREACHED(); +} + +bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) { + return blobs_inuse_map_.find(uuid) != blobs_inuse_map_.end(); +} + +bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) { + return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); +} + +void BlobDispatcherHost::ClearHostFromBlobStorageContext() { + BlobStorageContext* context = this->context(); + for (const auto& url : public_blob_urls_) { + context->RevokePublicBlobURL(url); + } + for (const auto& uuid_refnum_pair : blobs_inuse_map_) { + for (int i = 0; i < uuid_refnum_pair.second; ++i) + context->DecrementBlobRefCount(uuid_refnum_pair.first); + } + async_builder_.CancelAll(context); +} + +} // namespace content diff --git a/chromium/content/browser/blob_storage/blob_dispatcher_host.h b/chromium/content/browser/blob_storage/blob_dispatcher_host.h new file mode 100644 index 00000000000..bf1a03cf8fb --- /dev/null +++ b/chromium/content/browser/blob_storage/blob_dispatcher_host.h @@ -0,0 +1,148 @@ +// 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 CONTENT_BROWSER_BLOB_STORAGE_BLOB_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_BLOB_STORAGE_BLOB_DISPATCHER_HOST_H_ + +#include +#include +#include + +#include "base/files/file.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory_handle.h" +#include "content/common/content_export.h" +#include "content/public/browser/browser_message_filter.h" +#include "storage/browser/blob/blob_async_builder_host.h" +#include "storage/browser/blob/blob_transport_result.h" +#include "storage/common/blob_storage/blob_storage_constants.h" + +class GURL; + +namespace IPC { +class Sender; +} + +namespace storage { +class DataElement; +class BlobDataBuilder; +struct BlobItemBytesRequest; +struct BlobItemBytesResponse; +class BlobStorageContext; +} + +namespace content { +class ChromeBlobStorageContext; + +// This class's responsibility is to listen for and dispatch blob storage +// messages and handle logistics of blob storage for a single child process. +// When the child process terminates all blob references attributable to +// that process go away upon destruction of the instance. +// This lives in the browser process, is single threaded (IO thread), and there +// is one per child process. +class CONTENT_EXPORT BlobDispatcherHost : public BrowserMessageFilter { + public: + explicit BlobDispatcherHost(ChromeBlobStorageContext* blob_storage_context); + + // BrowserMessageFilter implementation. + void OnChannelClosing() override; + bool OnMessageReceived(const IPC::Message& message) override; + + protected: + ~BlobDispatcherHost() override; + + // For testing use only. + void SetMemoryConstantsForTesting(size_t max_ipc_memory_size, + size_t max_shared_memory_size, + uint64_t max_file_size) { + async_builder_.SetMemoryConstantsForTesting( + max_ipc_memory_size, max_shared_memory_size, max_file_size); + } + + private: + friend class base::RefCountedThreadSafe; + friend class BlobDispatcherHostTest; + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, EmptyUUIDs); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, MultipleTransfers); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, SharedMemoryTransfer); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, OnCancelBuildingBlob); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + BlobReferenceWhileConstructing); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + BlobReferenceWhileShortcutConstructing); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + BlobReferenceWhileConstructingCancelled); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, DecrementRefAfterRegister); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, DecrementRefAfterOnStart); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + DecrementRefAfterOnStartWithHandle); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + HostDisconnectAfterRegisterWithHandle); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, HostDisconnectAfterOnStart); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + HostDisconnectAfterOnMemoryResponse); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + CreateBlobWithBrokenReference); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + DeferenceBlobOnDifferentHost); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, BuildingReferenceChain); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + BuildingReferenceChainWithCancel); + FRIEND_TEST_ALL_PREFIXES(BlobDispatcherHostTest, + BuildingReferenceChainWithSourceDeath); + + typedef std::map BlobReferenceMap; + + void OnRegisterBlobUUID(const std::string& uuid, + const std::string& content_type, + const std::string& content_disposition, + const std::set& referenced_blob_uuids); + void OnStartBuildingBlob( + const std::string& uuid, + const std::vector& descriptions); + void OnMemoryItemResponse( + const std::string& uuid, + const std::vector& response); + void OnCancelBuildingBlob(const std::string& uuid, + const storage::IPCBlobCreationCancelCode code); + + void OnIncrementBlobRefCount(const std::string& uuid); + void OnDecrementBlobRefCount(const std::string& uuid); + void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid); + void OnRevokePublicBlobURL(const GURL& public_url); + + storage::BlobStorageContext* context(); + + void SendMemoryRequest( + const std::string& uuid, + scoped_ptr> requests, + scoped_ptr> memory_handles, + scoped_ptr> files); + + // Send the appropriate IPC response to the renderer for the given result. + void SendIPCResponse(const std::string& uuid, + storage::BlobTransportResult result); + + bool IsInUseInHost(const std::string& uuid); + bool IsUrlRegisteredInHost(const GURL& blob_url); + + // Unregisters all blobs and urls that were registered in this host. + void ClearHostFromBlobStorageContext(); + + // Collection of blob ids and a count of how many usages + // of that id are attributable to this consumer. + BlobReferenceMap blobs_inuse_map_; + + // The set of public blob urls coined by this consumer. + std::set public_blob_urls_; + + scoped_refptr blob_storage_context_; + storage::BlobAsyncBuilderHost async_builder_; + + DISALLOW_COPY_AND_ASSIGN(BlobDispatcherHost); +}; +} // namespace content +#endif // CONTENT_BROWSER_BLOB_STORAGE_BLOB_DISPATCHER_HOST_H_ diff --git a/chromium/content/browser/blob_storage/blob_dispatcher_host_unittest.cc b/chromium/content/browser/blob_storage/blob_dispatcher_host_unittest.cc new file mode 100644 index 00000000000..43cdf8ad373 --- /dev/null +++ b/chromium/content/browser/blob_storage/blob_dispatcher_host_unittest.cc @@ -0,0 +1,1199 @@ +// 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 "content/browser/blob_storage/blob_dispatcher_host.h" + +#include + +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/run_loop.h" +#include "base/tuple.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" +#include "content/common/fileapi/webblob_messages.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "ipc/ipc_sender.h" +#include "ipc/ipc_test_sink.h" +#include "ipc/message_filter.h" +#include "storage/browser/blob/blob_data_builder.h" +#include "storage/browser/blob/blob_data_handle.h" +#include "storage/browser/blob/blob_storage_context.h" +#include "storage/common/blob_storage/blob_item_bytes_request.h" +#include "storage/common/blob_storage/blob_item_bytes_response.h" +#include "storage/common/data_element.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using storage::BlobDataBuilder; +using storage::BlobDataHandle; +using storage::BlobItemBytesRequest; +using storage::BlobItemBytesResponse; +using storage::BlobStorageContext; +using storage::BlobTransportResult; +using storage::DataElement; +using storage::IPCBlobCreationCancelCode; +using RequestMemoryCallback = + storage::BlobAsyncBuilderHost::RequestMemoryCallback; + +namespace content { +namespace { + +const char kContentType[] = "text/plain"; +const char kContentDisposition[] = "content_disposition"; +const char kData[] = "data!!"; +const size_t kDataSize = 6; + +const size_t kTestBlobStorageIPCThresholdBytes = 20; +const size_t kTestBlobStorageMaxSharedMemoryBytes = 50; +const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; + +void ConstructionCompletePopulator(bool* succeeded_pointer, + IPCBlobCreationCancelCode* reason_pointer, + bool succeeded, + IPCBlobCreationCancelCode reason) { + *succeeded_pointer = succeeded; + *reason_pointer = reason; +} + +class TestableBlobDispatcherHost : public BlobDispatcherHost { + public: + TestableBlobDispatcherHost(ChromeBlobStorageContext* blob_storage_context, + IPC::TestSink* sink) + : BlobDispatcherHost(blob_storage_context), sink_(sink) { + this->SetMemoryConstantsForTesting(kTestBlobStorageIPCThresholdBytes, + kTestBlobStorageMaxSharedMemoryBytes, + kTestBlobStorageMaxFileSizeBytes); + } + + bool Send(IPC::Message* message) override { return sink_->Send(message); } + + void ShutdownForBadMessage() override { shutdown_for_bad_message_ = true; } + + bool shutdown_for_bad_message_ = false; + + protected: + ~TestableBlobDispatcherHost() override {} + + private: + friend class base::RefCountedThreadSafe; + + IPC::TestSink* sink_; +}; + +} // namespace + +class BlobDispatcherHostTest : public testing::Test { + protected: + BlobDispatcherHostTest() + : chrome_blob_storage_context_( + ChromeBlobStorageContext::GetFor(&browser_context_)) { + host_ = + new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_); + } + ~BlobDispatcherHostTest() override {} + + void SetUp() override { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { + command_line->AppendSwitch(switches::kDisableKillAfterBadIPC); + } + // We run the run loop to initialize the chrome blob storage context. + base::RunLoop().RunUntilIdle(); + context_ = chrome_blob_storage_context_->context(); + DCHECK(context_); + } + + void ExpectBlobNotExist(const std::string& id) { + EXPECT_FALSE(context_->registry().HasEntry(id)); + EXPECT_FALSE(host_->IsInUseInHost(id)); + EXPECT_FALSE(IsBeingBuiltInHost(id)); + } + + void AsyncShortcutBlobTransfer(const std::string& id) { + sink_.ClearMessages(); + ExpectBlobNotExist(id); + host_->OnRegisterBlobUUID(id, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + DataElement element; + element.SetToBytes(kData, kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(id, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + ExpectDone(id); + sink_.ClearMessages(); + } + + void AsyncBlobTransfer(const std::string& id) { + sink_.ClearMessages(); + ExpectBlobNotExist(id); + host_->OnRegisterBlobUUID(id, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(id, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + // Expect our request. + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + ExpectRequest(id, expected_requests); + sink_.ClearMessages(); + + // Send results; + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + host_->OnMemoryItemResponse(id, responses); + ExpectDone(id); + sink_.ClearMessages(); + } + + void ExpectAndResetBadMessage() { + EXPECT_TRUE(host_->shutdown_for_bad_message_); + host_->shutdown_for_bad_message_ = false; + } + + void ExpectHandleEqualsData(BlobDataHandle* handle, + const std::vector& data) { + scoped_ptr snapshot = handle->CreateSnapshot(); + EXPECT_FALSE(handle->IsBeingBuilt()); + for (size_t i = 0; i < data.size(); i++) { + const DataElement& expected = data[i]; + const DataElement& actual = snapshot->items()[i]->data_element(); + EXPECT_EQ(expected, actual); + } + EXPECT_EQ(data.size(), snapshot->items().size()); + } + + void ExpectRequest( + const std::string& expected_uuid, + const std::vector& expected_requests) { + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); + const IPC::Message* message = + sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID); + ASSERT_TRUE(message); + base::Tuple, + std::vector, + std::vector> + args; + BlobStorageMsg_RequestMemoryItem::Read(message, &args); + EXPECT_EQ(expected_uuid, base::get<0>(args)); + std::vector requests = base::get<1>(args); + ASSERT_EQ(requests.size(), expected_requests.size()); + for (size_t i = 0; i < expected_requests.size(); ++i) { + EXPECT_EQ(expected_requests[i], requests[i]); + } + } + + void ExpectRequestWithSharedMemoryHandles( + const std::string& expected_uuid, + const std::vector& expected_requests, + std::vector* shared_memory_handles) { + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); + const IPC::Message* message = + sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID); + ASSERT_TRUE(message); + base::Tuple, + std::vector, + std::vector> + args; + BlobStorageMsg_RequestMemoryItem::Read(message, &args); + EXPECT_EQ(expected_uuid, base::get<0>(args)); + std::vector requests = base::get<1>(args); + ASSERT_EQ(requests.size(), expected_requests.size()); + for (size_t i = 0; i < expected_requests.size(); ++i) { + EXPECT_EQ(expected_requests[i], requests[i]); + } + *shared_memory_handles = std::move(base::get<2>(args)); + } + + void ExpectCancel(const std::string& expected_uuid, + IPCBlobCreationCancelCode code) { + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); + const IPC::Message* message = + sink_.GetUniqueMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID); + ASSERT_TRUE(message); + base::Tuple args; + BlobStorageMsg_CancelBuildingBlob::Read(message, &args); + EXPECT_EQ(expected_uuid, base::get<0>(args)); + EXPECT_EQ(code, base::get<1>(args)); + } + + void ExpectDone(const std::string& expected_uuid) { + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); + EXPECT_FALSE( + sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); + const IPC::Message* message = + sink_.GetUniqueMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID); + base::Tuple args; + BlobStorageMsg_DoneBuildingBlob::Read(message, &args); + EXPECT_EQ(expected_uuid, base::get<0>(args)); + } + + bool IsBeingBuiltInHost(const std::string& uuid) { + return host_->async_builder_.IsBeingBuilt(uuid); + } + + IPC::TestSink sink_; + TestBrowserThreadBundle browser_thread_bundle_; + TestBrowserContext browser_context_; + ChromeBlobStorageContext* chrome_blob_storage_context_; + BlobStorageContext* context_ = nullptr; + scoped_refptr host_; +}; + +TEST_F(BlobDispatcherHostTest, EmptyUUIDs) { + host_->OnRegisterBlobUUID("", "", "", std::set()); + ExpectAndResetBadMessage(); + host_->OnStartBuildingBlob("", std::vector()); + ExpectAndResetBadMessage(); + host_->OnMemoryItemResponse("", std::vector()); + ExpectAndResetBadMessage(); + host_->OnCancelBuildingBlob("", IPCBlobCreationCancelCode::UNKNOWN); + ExpectAndResetBadMessage(); +} + +TEST_F(BlobDispatcherHostTest, Shortcut) { + const std::string kId = "uuid1"; + AsyncShortcutBlobTransfer(kId); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + scoped_ptr handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle); + + DataElement expected; + expected.SetToBytes(kData, kDataSize); + std::vector elements = {expected}; + ExpectHandleEqualsData(handle.get(), elements); +} + +TEST_F(BlobDispatcherHostTest, RegularTransfer) { + const std::string kId = "uuid1"; + AsyncBlobTransfer(kId); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + scoped_ptr handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle); + + DataElement expected; + expected.SetToBytes(kData, kDataSize); + std::vector elements = {expected}; + ExpectHandleEqualsData(handle.get(), elements); +} + +TEST_F(BlobDispatcherHostTest, MultipleTransfers) { + const std::string kId = "uuid"; + const int kNumIters = 10; + for (int i = 0; i < kNumIters; i++) { + std::string id = kId; + id += ('0' + i); + ExpectBlobNotExist(id); + host_->OnRegisterBlobUUID(id, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + } + sink_.ClearMessages(); + for (int i = 0; i < kNumIters; i++) { + std::string id = kId; + id += ('0' + i); + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(id, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + // Expect our request. + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + ExpectRequest(id, expected_requests); + sink_.ClearMessages(); + } + for (int i = 0; i < kNumIters; i++) { + std::string id = kId; + id += ('0' + i); + // Send results; + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + host_->OnMemoryItemResponse(id, responses); + ExpectDone(id); + sink_.ClearMessages(); + } +} + +TEST_F(BlobDispatcherHostTest, SharedMemoryTransfer) { + const std::string kId = "uuid1"; + const size_t kLargeSize = kTestBlobStorageMaxSharedMemoryBytes * 2; + std::vector shared_memory_handles; + + ExpectBlobNotExist(kId); + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + // Grab the handle. + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + bool built = false; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + EXPECT_FALSE(built); + + EXPECT_FALSE(host_->shutdown_for_bad_message_); + DataElement element; + element.SetToBytesDescription(kLargeSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + // Expect our first request. + std::vector expected_requests = { + BlobItemBytesRequest::CreateSharedMemoryRequest( + 0 /* request_number */, 0 /* renderer_item_index */, + 0 /* renderer_item_offset */, + static_cast( + kTestBlobStorageMaxSharedMemoryBytes) /* size */, + 0 /* handle_index */, 0 /* handle_offset */)}; + ExpectRequestWithSharedMemoryHandles(kId, expected_requests, + &shared_memory_handles); + sink_.ClearMessages(); + + // Populate the shared memory. + EXPECT_EQ(1u, shared_memory_handles.size()); + EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_memory_handles[0])); + { + base::SharedMemory memory( + base::SharedMemory::DuplicateHandle(shared_memory_handles[0]), false); + memory.Map(kTestBlobStorageMaxSharedMemoryBytes); + std::memset(memory.memory(), 'X', kTestBlobStorageMaxSharedMemoryBytes); + memory.Close(); + } + + // Send the confirmation. + std::vector responses = {BlobItemBytesResponse(0)}; + host_->OnMemoryItemResponse(kId, responses); + + // Expect our second request. + expected_requests = {BlobItemBytesRequest::CreateSharedMemoryRequest( + 1 /* request_number */, 0 /* renderer_item_index */, + static_cast( + kTestBlobStorageMaxSharedMemoryBytes) /* renderer_item_offset */, + static_cast(kTestBlobStorageMaxSharedMemoryBytes) /* size */, + 0 /* handle_index */, 0 /* handle_offset */)}; + ExpectRequestWithSharedMemoryHandles(kId, expected_requests, + &shared_memory_handles); + sink_.ClearMessages(); + + // Populate the shared memory. + EXPECT_EQ(1u, shared_memory_handles.size()); + EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_memory_handles[0])); + { + base::SharedMemory memory( + base::SharedMemory::DuplicateHandle(shared_memory_handles[0]), false); + memory.Map(kTestBlobStorageMaxSharedMemoryBytes); + std::memset(memory.memory(), 'Z', kTestBlobStorageMaxSharedMemoryBytes); + memory.Close(); + } + // Send the confirmation. + responses = {BlobItemBytesResponse(1)}; + host_->OnMemoryItemResponse(kId, responses); + + ExpectDone(kId); + sink_.ClearMessages(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(built) << "Error code: " << static_cast(error_code); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + scoped_ptr handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle); + + DataElement expected; + expected.SetToAllocatedBytes(kLargeSize / 2); + std::memset(expected.mutable_bytes(), 'X', kLargeSize / 2); + elements = {expected}; + std::memset(expected.mutable_bytes(), 'Z', kLargeSize / 2); + elements.push_back(expected); + ExpectHandleEqualsData(handle.get(), elements); +} + +TEST_F(BlobDispatcherHostTest, OnCancelBuildingBlob) { + const std::string kId("id"); + // We ignore blobs that are unknown, as it could have been cancelled earlier + // and the renderer didn't know about it yet. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + // Start building blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + // It should have requested memory here. + EXPECT_FALSE(host_->shutdown_for_bad_message_); + sink_.ClearMessages(); + + // Cancel in middle of construction. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(host_->IsInUseInHost(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + // Check that's it's broken. + scoped_ptr handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle->IsBroken()); + handle.reset(); + base::RunLoop().RunUntilIdle(); + + // Get rid of it in the host. + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + ExpectBlobNotExist(kId); + + // Create blob again to verify we don't have any old construction state lying + // around. + AsyncBlobTransfer(kId); + + // Check data. + handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle); + DataElement expected; + expected.SetToBytes(kData, kDataSize); + std::vector expecteds = {expected}; + ExpectHandleEqualsData(handle.get(), expecteds); + + // Verify we can't cancel after the fact. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + ExpectAndResetBadMessage(); +} + +TEST_F(BlobDispatcherHostTest, BlobDataWithHostDeletion) { + // Build up a basic blob. + const std::string kId("id"); + AsyncShortcutBlobTransfer(kId); + scoped_ptr handle = context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(handle); + + // Kill the host. + host_ = nullptr; + base::RunLoop().RunUntilIdle(); + // Should still be there due to the handle. + scoped_ptr another_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(another_handle); + + // Should disappear after dropping both handles. + handle.reset(); + another_handle.reset(); + base::RunLoop().RunUntilIdle(); + + handle = context_->GetBlobDataFromUUID(kId); + EXPECT_FALSE(handle); +} + +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructing) { + const std::string kId("id"); + + // Start building blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + // Grab the handle. + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(blob_data_handle); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + bool built = false; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + // Continue building. + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + sink_.ClearMessages(); + + // Send data. + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + sink_.ClearMessages(); + host_->OnMemoryItemResponse(kId, responses); + + ExpectDone(kId); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(built) << "Error code: " << static_cast(error_code); +} + +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileShortcutConstructing) { + const std::string kId("id"); + + // Start building blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + // Grab the handle. + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(blob_data_handle); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + bool built = false; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + // Continue building. + DataElement element; + element.SetToBytes(kData, kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + ExpectDone(kId); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(built) << "Error code: " << static_cast(error_code); +} + +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructingCancelled) { + const std::string kId("id"); + + // Start building blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + // Grab the handle. + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(blob_data_handle); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + bool built = true; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + // Cancel in middle of construction. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(host_->IsInUseInHost(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + EXPECT_TRUE(blob_data_handle->IsBroken()); + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::UNKNOWN, error_code); + error_code = IPCBlobCreationCancelCode::UNKNOWN; + built = true; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::UNKNOWN, error_code); + + // Remove it. + blob_data_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + host_->OnDecrementBlobRefCount(kId); + ExpectBlobNotExist(kId); +} + +TEST_F(BlobDispatcherHostTest, DecrementRefAfterRegister) { + const std::string kId("id"); + // Decrement the refcount while building (renderer blob gc'd before + // construction was completed). + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + ExpectCancel(kId, + IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); + sink_.ClearMessages(); + + // Do the same, but this time grab a handle before we decrement. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + host_->OnDecrementBlobRefCount(kId); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(IsBeingBuiltInHost(kId)); + + // Finish up the blob, and verify we got the done message. + DataElement element; + element.SetToBytes(kData, kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + ExpectDone(kId); + sink_.ClearMessages(); + // Get rid of the handle, and verify it's gone. + blob_data_handle.reset(); + base::RunLoop().RunUntilIdle(); + // Check that it's no longer around. + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); +} + +TEST_F(BlobDispatcherHostTest, DecrementRefAfterOnStart) { + const std::string kId("id"); + + // Decrement the refcount while building, after we call OnStartBuildlingBlob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + ExpectCancel(kId, + IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); + sink_.ClearMessages(); + + // Do the same, but this time grab a handle to keep it alive. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + // Grab the handle before decrementing. + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + host_->OnDecrementBlobRefCount(kId); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(IsBeingBuiltInHost(kId)); + + // We finish the blob, and verify that we send 'Done' back to the renderer. + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + host_->OnMemoryItemResponse(kId, responses); + ExpectDone(kId); + sink_.ClearMessages(); + // Check that it's still around. + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + + // Get rid of the handle, and verify it's gone. + blob_data_handle.reset(); + base::RunLoop().RunUntilIdle(); + // Check that it's no longer around. + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); +} + +TEST_F(BlobDispatcherHostTest, DecrementRefAfterOnStartWithHandle) { + const std::string kId("id"); + // Decrement the refcount while building, after we call + // OnStartBuildlingBlob, except we have another handle. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + bool built = true; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + + // Check that we got the expected request. + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(IsBeingBuiltInHost(kId)); + // Decrement, simulating where the ref goes out of scope in renderer. + host_->OnDecrementBlobRefCount(kId); + // We still have the blob as it's not done. + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + EXPECT_TRUE(IsBeingBuiltInHost(kId)); + // Cancel to clean up. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + // Run loop to propagate the handle decrement in the host. + base::RunLoop().RunUntilIdle(); + // We still have the entry because of our earlier handle. + EXPECT_TRUE(context_->registry().HasEntry(kId)); + EXPECT_FALSE(IsBeingBuiltInHost(kId)); + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::UNKNOWN, error_code); + sink_.ClearMessages(); + + // Should disappear after dropping the handle. + EXPECT_TRUE(blob_data_handle->IsBroken()); + blob_data_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->registry().HasEntry(kId)); +} + +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterRegisterWithHandle) { + const std::string kId("id"); + + // Delete host with a handle to the blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + scoped_ptr blob_data_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); + bool built = true; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + blob_data_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + // Get rid of host, which was doing the constructing. + host_ = nullptr; + EXPECT_FALSE(blob_data_handle->IsBeingBuilt()); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT, error_code); + + // Should still be there due to the handle. + scoped_ptr another_handle = + context_->GetBlobDataFromUUID(kId); + EXPECT_TRUE(another_handle); + + // Should disappear after dropping both handles. + blob_data_handle.reset(); + another_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->registry().HasEntry(kId)); +} + +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterOnStart) { + const std::string kId("id"); + + // Host deleted after OnStartBuilding. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + host_->OnStartBuildingBlob(kId, elements); + + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + host_ = nullptr; + // We need to run the message loop because of the handle in the async builder. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->registry().HasEntry(kId)); +} + +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterOnMemoryResponse) { + const std::string kId("id"); + + // Host deleted after OnMemoryItemResponse. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + + // Create list of two items. + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element, element}; + host_->OnStartBuildingBlob(kId, elements); + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize), + BlobItemBytesRequest::CreateIPCRequest(1, 1, 0, kDataSize)}; + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + + // Send just one response so the blob isn't 'done' yet. + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + host_->OnMemoryItemResponse(kId, responses); + EXPECT_EQ(0u, sink_.message_count()); + + host_ = nullptr; + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->registry().HasEntry(kId)); +} + +TEST_F(BlobDispatcherHostTest, CreateBlobWithBrokenReference) { + const std::string kBrokenId("id1"); + const std::string kReferencingId("id2"); + + // First, let's test a circular reference. + const std::string kCircularId("id1"); + host_->OnRegisterBlobUUID(kCircularId, std::string(kContentType), + std::string(kContentDisposition), {kCircularId}); + ExpectAndResetBadMessage(); + + // Next, test a blob that references a broken blob. + host_->OnRegisterBlobUUID(kBrokenId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + host_->OnCancelBuildingBlob(kBrokenId, IPCBlobCreationCancelCode::UNKNOWN); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + EXPECT_TRUE(context_->GetBlobDataFromUUID(kBrokenId)->IsBroken()); + + // Create referencing blob. We should be broken right away, but also ignore + // the subsequent OnStart message. + host_->OnRegisterBlobUUID(kReferencingId, std::string(kContentType), + std::string(kContentDisposition), {kBrokenId}); + EXPECT_TRUE(context_->GetBlobDataFromUUID(kReferencingId)->IsBroken()); + EXPECT_FALSE(IsBeingBuiltInHost(kReferencingId)); + EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); + ExpectCancel(kReferencingId, + IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); + sink_.ClearMessages(); + + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + element.SetToBlob(kBrokenId); + elements.push_back(element); + host_->OnStartBuildingBlob(kReferencingId, elements); + EXPECT_EQ(0u, sink_.message_count()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(BlobDispatcherHostTest, DeferenceBlobOnDifferentHost) { + const std::string kId("id"); + // Data elements for our transfer & checking messages. + DataElement element; + element.SetToBytesDescription(kDataSize); + std::vector elements = {element}; + std::vector expected_requests = { + BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; + BlobItemBytesResponse response(0); + std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); + std::vector responses = {response}; + + scoped_refptr host2( + new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_)); + + // Delete host with another host having a referencing, then dereference on + // second host. Verify we're still building it on first host, and then + // verify that a building message from the renderer will kill it. + + // Test OnStartBuilding after double dereference. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + host2->OnIncrementBlobRefCount(kId); + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(host_->IsInUseInHost(kId)); + host2->OnDecrementBlobRefCount(kId); + // So no more blob in the context, but we're still being built in host 1. + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); + host_->OnStartBuildingBlob(kId, elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + // We should be cleaned up. + EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); + ExpectCancel(kId, + IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); + sink_.ClearMessages(); + + // Same as above, but test OnMemoryItemResponse after double dereference. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + host2->OnIncrementBlobRefCount(kId); + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(host_->IsInUseInHost(kId)); + host_->OnStartBuildingBlob(kId, elements); + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + + host2->OnDecrementBlobRefCount(kId); + // So no more blob in the context, but we're still being built in host 1. + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); + host_->OnMemoryItemResponse(kId, responses); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + // We should be cleaned up. + EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); + ExpectCancel(kId, + IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); + sink_.ClearMessages(); + + // Same, but now for OnCancel. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + host2->OnIncrementBlobRefCount(kId); + host_->OnDecrementBlobRefCount(kId); + EXPECT_FALSE(host_->IsInUseInHost(kId)); + host_->OnStartBuildingBlob(kId, elements); + ExpectRequest(kId, expected_requests); + sink_.ClearMessages(); + + host2->OnDecrementBlobRefCount(kId); + // So no more blob in the context, but we're still being built in host 1. + EXPECT_FALSE(context_->registry().HasEntry(kId)); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + // We should be cleaned up. + EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); +} + +TEST_F(BlobDispatcherHostTest, BuildingReferenceChain) { + const std::string kId("id"); + const std::string kSameHostReferencingId("id2"); + const std::string kDifferentHostReferencingId("id3"); + // Data elements for our transfer & checking messages. + DataElement element; + element.SetToBytes(kData, kDataSize); + std::vector elements = {element}; + DataElement referencing_element; + referencing_element.SetToBlob(kId); + std::vector referencing_elements = {referencing_element}; + std::set referenced_blobs_set = {kId}; + + scoped_refptr host2( + new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_)); + + // We want to have a blob referencing another blob that is building, both on + // the same host and a different host. We should successfully build all blobs + // after the referenced blob is finished. + + // First we start the referenced blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_TRUE(host_->IsInUseInHost(kId)); + + // Next we start the referencing blobs in both the same and different host. + host_->OnRegisterBlobUUID(kSameHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), + referenced_blobs_set); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + host_->OnStartBuildingBlob(kSameHostReferencingId, referencing_elements); + EXPECT_FALSE(host_->shutdown_for_bad_message_); + ExpectDone(kSameHostReferencingId); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kSameHostReferencingId)); + sink_.ClearMessages(); + // Now the other host. + host2->OnRegisterBlobUUID( + kDifferentHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), referenced_blobs_set); + EXPECT_FALSE(host2->shutdown_for_bad_message_); + host2->OnStartBuildingBlob(kDifferentHostReferencingId, referencing_elements); + EXPECT_FALSE(host2->shutdown_for_bad_message_); + ExpectDone(kDifferentHostReferencingId); + EXPECT_TRUE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + sink_.ClearMessages(); + + // Now we finish the first blob, and we expect all blobs to finish. + host_->OnStartBuildingBlob(kId, elements); + ExpectDone(kId); + // We need to run the message loop to propagate the construction callbacks. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kSameHostReferencingId)); + EXPECT_FALSE( + context_->GetBlobDataFromUUID(kSameHostReferencingId)->IsBroken()); + EXPECT_FALSE( + context_->GetBlobDataFromUUID(kDifferentHostReferencingId)->IsBroken()); + sink_.ClearMessages(); + + // Finally check that our data is correct in the child elements. + scoped_ptr handle = + context_->GetBlobDataFromUUID(kDifferentHostReferencingId); + ExpectHandleEqualsData(handle.get(), elements); +} + +TEST_F(BlobDispatcherHostTest, BuildingReferenceChainWithCancel) { + const std::string kId("id"); + const std::string kSameHostReferencingId("id2"); + const std::string kDifferentHostReferencingId("id3"); + // Data elements for our transfer & checking messages. + DataElement referencing_element; + referencing_element.SetToBlob(kId); + std::vector referencing_elements = {referencing_element}; + std::set referenced_blobs_set = {kId}; + + scoped_refptr host2( + new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_)); + + // We want to have a blob referencing another blob that is building, both on + // the same host and a different host. After we cancel the first blob, the + // others should cancel as well. + + // First we start the referenced blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_TRUE(host_->IsInUseInHost(kId)); + + // Next we start the referencing blobs in both the same and different host. + host_->OnRegisterBlobUUID(kSameHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), + referenced_blobs_set); + host_->OnStartBuildingBlob(kSameHostReferencingId, referencing_elements); + ExpectDone(kSameHostReferencingId); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kSameHostReferencingId)); + sink_.ClearMessages(); + // Now the other host. + host2->OnRegisterBlobUUID( + kDifferentHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), referenced_blobs_set); + host2->OnStartBuildingBlob(kDifferentHostReferencingId, referencing_elements); + ExpectDone(kDifferentHostReferencingId); + EXPECT_TRUE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + sink_.ClearMessages(); + bool built = false; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + context_->GetBlobDataFromUUID(kDifferentHostReferencingId) + ->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + // Now we cancel the first blob, and we expect all blobs to cancel. + host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); + // We need to run the message loop to propagate the construction callbacks. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kSameHostReferencingId)); + EXPECT_TRUE( + context_->GetBlobDataFromUUID(kSameHostReferencingId)->IsBroken()); + EXPECT_TRUE( + context_->GetBlobDataFromUUID(kDifferentHostReferencingId)->IsBroken()); + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN, error_code); + sink_.ClearMessages(); +} + +TEST_F(BlobDispatcherHostTest, BuildingReferenceChainWithSourceDeath) { + const std::string kId("id"); + const std::string kSameHostReferencingId("id2"); + const std::string kDifferentHostReferencingId("id3"); + // Data elements for our transfer & checking messages. + DataElement referencing_element; + referencing_element.SetToBlob(kId); + std::vector referencing_elements = {referencing_element}; + std::set referenced_blobs_set = {kId}; + + scoped_refptr host2( + new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_)); + + // We want to have a blob referencing another blob that is building, both on + // the same host and a different host. When we destroy the host, the other + // blob should cancel, as well as the blob on the other host. + + // First we start the referenced blob. + host_->OnRegisterBlobUUID(kId, std::string(kContentType), + std::string(kContentDisposition), + std::set()); + EXPECT_TRUE(host_->IsInUseInHost(kId)); + + // Next we start the referencing blobs in both the same and different host. + host_->OnRegisterBlobUUID(kSameHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), + referenced_blobs_set); + host_->OnStartBuildingBlob(kSameHostReferencingId, referencing_elements); + ExpectDone(kSameHostReferencingId); + EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kSameHostReferencingId)); + sink_.ClearMessages(); + // Now the other host. + host2->OnRegisterBlobUUID( + kDifferentHostReferencingId, std::string(kContentType), + std::string(kContentDisposition), referenced_blobs_set); + host2->OnStartBuildingBlob(kDifferentHostReferencingId, referencing_elements); + ExpectDone(kDifferentHostReferencingId); + EXPECT_TRUE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + sink_.ClearMessages(); + + // Grab handles & add listeners. + bool built = true; + IPCBlobCreationCancelCode error_code = IPCBlobCreationCancelCode::UNKNOWN; + scoped_ptr blob_handle = context_->GetBlobDataFromUUID(kId); + blob_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &built, &error_code)); + + bool same_host_built = true; + IPCBlobCreationCancelCode same_host_error_code = + IPCBlobCreationCancelCode::UNKNOWN; + scoped_ptr same_host_blob_handle = + context_->GetBlobDataFromUUID(kSameHostReferencingId); + same_host_blob_handle->RunOnConstructionComplete(base::Bind( + &ConstructionCompletePopulator, &same_host_built, &same_host_error_code)); + + bool other_host_built = true; + IPCBlobCreationCancelCode other_host_error_code = + IPCBlobCreationCancelCode::UNKNOWN; + scoped_ptr other_host_blob_handle = + context_->GetBlobDataFromUUID(kDifferentHostReferencingId); + other_host_blob_handle->RunOnConstructionComplete( + base::Bind(&ConstructionCompletePopulator, &other_host_built, + &other_host_error_code)); + + // Now we kill the host. + host_ = nullptr; + // We need to run the message loop to propagate the construction callbacks. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host2->async_builder_.IsBeingBuilt(kDifferentHostReferencingId)); + EXPECT_TRUE( + context_->GetBlobDataFromUUID(kSameHostReferencingId)->IsBroken()); + EXPECT_TRUE( + context_->GetBlobDataFromUUID(kDifferentHostReferencingId)->IsBroken()); + + // Check our callbacks + EXPECT_FALSE(built); + EXPECT_EQ(IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT, error_code); + EXPECT_FALSE(same_host_built); + EXPECT_EQ(IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT, + same_host_error_code); + EXPECT_FALSE(other_host_built); + EXPECT_EQ(IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT, + other_host_error_code); + + sink_.ClearMessages(); +} + +} // namespace content diff --git a/chromium/content/browser/blob_storage/blob_storage_registry_unittest.cc b/chromium/content/browser/blob_storage/blob_storage_registry_unittest.cc index 9ff10d69700..918554202b0 100644 --- a/chromium/content/browser/blob_storage/blob_storage_registry_unittest.cc +++ b/chromium/content/browser/blob_storage/blob_storage_registry_unittest.cc @@ -15,36 +15,33 @@ using BlobState = BlobStorageRegistry::BlobState; TEST(BlobStorageRegistry, UUIDRegistration) { const std::string kBlob1 = "Blob1"; + const std::string kType = "type1"; + const std::string kDisposition = "disp1"; BlobStorageRegistry registry; EXPECT_FALSE(registry.DeleteEntry(kBlob1)); EXPECT_EQ(0u, registry.blob_count()); - Entry* entry = registry.CreateEntry(kBlob1); + Entry* entry = registry.CreateEntry(kBlob1, kType, kDisposition); ASSERT_NE(nullptr, entry); - EXPECT_EQ(BlobState::RESERVED, entry->state); + EXPECT_EQ(BlobState::PENDING, entry->state); + EXPECT_EQ(kType, entry->content_type); + EXPECT_EQ(kDisposition, entry->content_disposition); EXPECT_EQ(1u, entry->refcount); - EXPECT_FALSE(entry->exceeded_memory); EXPECT_FALSE(entry->data.get() || entry->data_builder.get()); - EXPECT_EQ(0u, entry->construction_complete_callbacks.size()); + EXPECT_EQ(0u, entry->build_completion_callbacks.size()); EXPECT_EQ(entry, registry.GetEntry(kBlob1)); EXPECT_TRUE(registry.DeleteEntry(kBlob1)); - entry = registry.CreateEntry(kBlob1); + entry = registry.CreateEntry(kBlob1, kType, kDisposition); - EXPECT_TRUE(entry->TestAndSetState(BlobState::RESERVED, - BlobState::ASYNC_TRANSPORTATION)); - EXPECT_FALSE( - entry->TestAndSetState(BlobState::CONSTRUCTION, BlobState::RESERVED)); - EXPECT_FALSE(entry->TestAndSetState(BlobState::ACTIVE, BlobState::RESERVED)); - EXPECT_TRUE(entry->TestAndSetState(BlobState::ASYNC_TRANSPORTATION, - BlobState::CONSTRUCTION)); - EXPECT_EQ(BlobState::CONSTRUCTION, entry->state); EXPECT_EQ(1u, registry.blob_count()); } TEST(BlobStorageRegistry, URLRegistration) { const std::string kBlob = "Blob1"; + const std::string kType = "type1"; + const std::string kDisposition = "disp1"; const std::string kBlob2 = "Blob2"; const GURL kURL = GURL("blob://Blob1"); const GURL kURL2 = GURL("blob://Blob2"); @@ -55,7 +52,7 @@ TEST(BlobStorageRegistry, URLRegistration) { EXPECT_FALSE(registry.DeleteURLMapping(kURL, nullptr)); EXPECT_FALSE(registry.CreateUrlMapping(kURL, kBlob)); EXPECT_EQ(0u, registry.url_count()); - Entry* entry = registry.CreateEntry(kBlob); + Entry* entry = registry.CreateEntry(kBlob, kType, kDisposition); EXPECT_FALSE(registry.IsURLMapped(kURL)); EXPECT_TRUE(registry.CreateUrlMapping(kURL, kBlob)); @@ -68,7 +65,7 @@ TEST(BlobStorageRegistry, URLRegistration) { EXPECT_EQ(kBlob, uuid); EXPECT_EQ(1u, registry.url_count()); - registry.CreateEntry(kBlob2); + registry.CreateEntry(kBlob2, kType, kDisposition); EXPECT_TRUE(registry.CreateUrlMapping(kURL2, kBlob2)); EXPECT_EQ(2u, registry.url_count()); EXPECT_TRUE(registry.DeleteURLMapping(kURL2, &uuid)); diff --git a/chromium/content/browser/bluetooth/README.md b/chromium/content/browser/bluetooth/README.md new file mode 100644 index 00000000000..6e55bcd73b3 --- /dev/null +++ b/chromium/content/browser/bluetooth/README.md @@ -0,0 +1,24 @@ +Bluetooth in Content +==================== + +`content/*/bluetooth` implements [Web Bluetooth][WB] using the +`device/bluetooth` code module. + +[WB]: https://webbluetoothcg.github.io/web-bluetooth/ + +Testing +-------------------------------------------------------------------------------- + +Bluetooth layout tests in `third_party/WebKit/LayoutTests/bluetooth/` rely on +fake Bluetooth implementation classes constructed in +`content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider`. +These tests span JavaScript binding to the `device/bluetooth` API layer. + + +Design Documents +-------------------------------------------------------------------------------- + +See: [Class Diagram of Web Bluetooth through Bluetooth Android][Class] + +[Class]: https://sites.google.com/a/chromium.org/dev/developers/design-documents/bluetooth-design-docs/web-bluetooth-through-bluetooth-android-class-diagram + diff --git a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.cc b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.cc index 9075fe82bb3..cd2e78929a3 100644 --- a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.cc +++ b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "content/browser/bluetooth/bluetooth_blacklist.h" #include "content/common/bluetooth/bluetooth_scan_filter.h" #include "crypto/random.h" #include "device/bluetooth/bluetooth_uuid.h" @@ -55,18 +56,28 @@ const std::string& BluetoothAllowedDevicesMap::AddDevice( // https://w3c.github.io/webappsec-secure-contexts/ CHECK(!origin.unique()); - if (ContainsKey(origin_to_device_address_to_id_map_[origin], - device_address)) { + auto device_address_to_id_map = origin_to_device_address_to_id_map_[origin]; + auto id_iter = device_address_to_id_map.find(device_address); + if (id_iter != device_address_to_id_map.end()) { VLOG(1) << "Device already in map of allowed devices."; + const auto& device_id = id_iter->second; + + AddUnionOfServicesTo( + filters, optional_services, + &origin_to_device_id_to_services_map_[origin][device_id]); + return origin_to_device_address_to_id_map_[origin][device_address]; } - const std::string device_id = GenerateDeviceId(origin); + const std::string device_id = GenerateDeviceId(); VLOG(1) << "Id generated for device: " << device_id; origin_to_device_address_to_id_map_[origin][device_address] = device_id; origin_to_device_id_to_address_map_[origin][device_id] = device_address; - origin_to_device_id_to_services_map_[origin][device_id] = - UnionOfServices(filters, optional_services); + AddUnionOfServicesTo( + filters, optional_services, + &origin_to_device_id_to_services_map_[origin][device_id]); + + CHECK(device_id_set_.insert(device_id).second); return origin_to_device_address_to_id_map_[origin][device_address]; } @@ -88,6 +99,9 @@ void BluetoothAllowedDevicesMap::RemoveDevice( CHECK(origin_to_device_id_to_address_map_.erase(origin)); CHECK(origin_to_device_id_to_services_map_.erase(origin)); } + + // 3. Remove from set of ids. + CHECK(device_id_set_.erase(device_id)); } const std::string& BluetoothAllowedDevicesMap::GetDeviceId( @@ -123,33 +137,48 @@ const std::string& BluetoothAllowedDevicesMap::GetDeviceAddress( : id_iter->second; } -std::string BluetoothAllowedDevicesMap::GenerateDeviceId( - const url::Origin& origin) { - std::string device_id = GetBase64Id(); - auto id_map_iter = origin_to_device_id_to_address_map_.find(origin); - if (id_map_iter == origin_to_device_id_to_address_map_.end()) { - return device_id; +bool BluetoothAllowedDevicesMap::IsOriginAllowedToAccessService( + const url::Origin& origin, + const std::string& device_id, + const std::string& service_uuid) const { + if (BluetoothBlacklist::Get().IsExcluded(BluetoothUUID(service_uuid))) + return false; + + auto id_map_iter = origin_to_device_id_to_services_map_.find(origin); + if (id_map_iter == origin_to_device_id_to_services_map_.end()) { + return false; } - while (ContainsKey(id_map_iter->second, device_id)) { + + const auto& device_id_to_services_map = id_map_iter->second; + + auto id_iter = device_id_to_services_map.find(device_id); + + return id_iter == device_id_to_services_map.end() + ? false + : ContainsKey(id_iter->second, service_uuid); +} + +std::string BluetoothAllowedDevicesMap::GenerateDeviceId() { + std::string device_id = GetBase64Id(); + while (ContainsKey(device_id_set_, device_id)) { LOG(WARNING) << "Generated repeated id."; device_id = GetBase64Id(); } return device_id; } -std::set BluetoothAllowedDevicesMap::UnionOfServices( +void BluetoothAllowedDevicesMap::AddUnionOfServicesTo( const std::vector& filters, - const std::vector& optional_services) { - std::set unionOfServices; + const std::vector& optional_services, + std::set* unionOfServices) { for (const auto& filter : filters) { for (const BluetoothUUID& uuid : filter.services) { - unionOfServices.insert(uuid.canonical_value()); + unionOfServices->insert(uuid.canonical_value()); } } for (const BluetoothUUID& uuid : optional_services) { - unionOfServices.insert(uuid.canonical_value()); + unionOfServices->insert(uuid.canonical_value()); } - return unionOfServices; } } // namespace content diff --git a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.h b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.h index d8672979eeb..32823593a37 100644 --- a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.h +++ b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map.h @@ -25,15 +25,14 @@ struct BluetoothScanFilter; // their services. // // |AddDevice| generates device ids, which are random strings that are unique -// for each (origin, device address) pair. +// in the map. class CONTENT_EXPORT BluetoothAllowedDevicesMap final { public: BluetoothAllowedDevicesMap(); ~BluetoothAllowedDevicesMap(); // Adds the Bluetooth Device with |device_address| to the map of allowed - // devices for that origin. Generates and returns a device id for the - // (|origin|, |device_address|) pair. + // devices for that origin. Generates and returns a device id. const std::string& AddDevice( const url::Origin& origin, const std::string& device_address, @@ -45,12 +44,8 @@ class CONTENT_EXPORT BluetoothAllowedDevicesMap final { void RemoveDevice(const url::Origin& origin, const std::string& device_address); - // TODO(ortuno): Add function to check if origin is allowed to access - // a device's service and add tests for that function. - // https://crbug.com/493460 - // Returns the Bluetooth Device's id for |origin|. Returns an empty string - // if the origin is not allowed to access the device. + // if |origin| is not allowed to access the device. const std::string& GetDeviceId(const url::Origin& origin, const std::string& device_address); @@ -59,17 +54,24 @@ class CONTENT_EXPORT BluetoothAllowedDevicesMap final { const std::string& GetDeviceAddress(const url::Origin& origin, const std::string& device_id); + // Returns true if the origin has previously been granted access to + // the service. + bool IsOriginAllowedToAccessService(const url::Origin& origin, + const std::string& device_id, + const std::string& service_uuid) const; + private: typedef std::map DeviceAddressToIdMap; typedef std::map DeviceIdToAddressMap; typedef std::map> DeviceIdToServicesMap; - // Returns an id guaranteed to be unique for the origin. The id is randomly + // Returns an id guaranteed to be unique for the map. The id is randomly // generated so that an origin can't guess the id used in another origin. - std::string GenerateDeviceId(const url::Origin& origin); - std::set UnionOfServices( + std::string GenerateDeviceId(); + void AddUnionOfServicesTo( const std::vector& filters, - const std::vector& optional_services); + const std::vector& optional_services, + std::set* unionOfServices); std::map origin_to_device_address_to_id_map_; @@ -77,6 +79,9 @@ class CONTENT_EXPORT BluetoothAllowedDevicesMap final { origin_to_device_id_to_address_map_; std::map origin_to_device_id_to_services_map_; + + // Keep track of all device_ids in the map. + std::set device_id_set_; }; } // namespace content diff --git a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc index 2d5243a1d5f..925d1a1393f 100644 --- a/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc +++ b/chromium/content/browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc @@ -10,153 +10,390 @@ #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +using device::BluetoothUUID; + namespace content { namespace { -const url::Origin test_origin1(GURL("https://www.example1.com")); -const url::Origin test_origin2(GURL("https://www.example2.com")); - -const std::string device_address1 = "00:00:00"; -const std::string device_address2 = "11:11:11"; - -const std::vector filters = +const url::Origin kTestOrigin1(GURL("https://www.example1.com")); +const url::Origin kTestOrigin2(GURL("https://www.example2.com")); + +const std::string kDeviceAddress1 = "00:00:00"; +const std::string kDeviceAddress2 = "11:11:11"; + +const char kGlucoseUUIDString[] = "00001808-0000-1000-8000-00805f9b34fb"; +const char kHeartRateUUIDString[] = "0000180d-0000-1000-8000-00805f9b34fb"; +const char kBatteryServiceUUIDString[] = "0000180f-0000-1000-8000-00805f9b34fb"; +const char kBloodPressureUUIDString[] = "00001813-0000-1000-8000-00805f9b34fb"; +const char kCyclingPowerUUIDString[] = "00001818-0000-1000-8000-00805f9b34fb"; +const BluetoothUUID kGlucoseUUID(kGlucoseUUIDString); +const BluetoothUUID kHeartRateUUID(kHeartRateUUIDString); +const BluetoothUUID kBatteryServiceUUID(kBatteryServiceUUIDString); +const BluetoothUUID kBloodPressureUUID(kBloodPressureUUIDString); +const BluetoothUUID kCyclingPowerUUID(kCyclingPowerUUIDString); + +const std::vector kEmptyFilters = std::vector(); -const std::vector optional_services = +const std::vector kEmptyOptionalServices = std::vector(); } // namespace -class BluetoothAllowedDevicesMapTest : public testing::Test {}; - -TEST_F(BluetoothAllowedDevicesMapTest, AddDeviceToMap) { +TEST(BluetoothAllowedDevicesMapTest, AddDeviceToMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); // Test that we can retrieve the device address/id. EXPECT_EQ(device_id, - allowed_devices_map.GetDeviceId(test_origin1, device_address1)); - EXPECT_EQ(device_address1, - allowed_devices_map.GetDeviceAddress(test_origin1, device_id)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress1)); + EXPECT_EQ(kDeviceAddress1, + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id)); } -TEST_F(BluetoothAllowedDevicesMapTest, AddDeviceToMapTwice) { +TEST(BluetoothAllowedDevicesMapTest, AddDeviceToMapTwice) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id1 = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); const std::string& device_id2 = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); EXPECT_EQ(device_id1, device_id2); // Test that we can retrieve the device address/id. EXPECT_EQ(device_id1, - allowed_devices_map.GetDeviceId(test_origin1, device_address1)); - EXPECT_EQ(device_address1, - allowed_devices_map.GetDeviceAddress(test_origin1, device_id1)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress1)); + EXPECT_EQ(kDeviceAddress1, + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id1)); } -TEST_F(BluetoothAllowedDevicesMapTest, AddTwoDevicesFromSameOriginToMap) { +TEST(BluetoothAllowedDevicesMapTest, AddTwoDevicesFromSameOriginToMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id1 = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); const std::string& device_id2 = allowed_devices_map.AddDevice( - test_origin1, device_address2, filters, optional_services); + kTestOrigin1, kDeviceAddress2, kEmptyFilters, kEmptyOptionalServices); EXPECT_NE(device_id1, device_id2); // Test that we can retrieve the device address/id. EXPECT_EQ(device_id1, - allowed_devices_map.GetDeviceId(test_origin1, device_address1)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress1)); EXPECT_EQ(device_id2, - allowed_devices_map.GetDeviceId(test_origin1, device_address2)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress2)); - EXPECT_EQ(device_address1, - allowed_devices_map.GetDeviceAddress(test_origin1, device_id1)); - EXPECT_EQ(device_address2, - allowed_devices_map.GetDeviceAddress(test_origin1, device_id2)); + EXPECT_EQ(kDeviceAddress1, + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id1)); + EXPECT_EQ(kDeviceAddress2, + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id2)); } -TEST_F(BluetoothAllowedDevicesMapTest, AddTwoDevicesFromTwoOriginsToMap) { +TEST(BluetoothAllowedDevicesMapTest, AddTwoDevicesFromTwoOriginsToMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id1 = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); const std::string& device_id2 = allowed_devices_map.AddDevice( - test_origin2, device_address2, filters, optional_services); + kTestOrigin2, kDeviceAddress2, kEmptyFilters, kEmptyOptionalServices); EXPECT_NE(device_id1, device_id2); // Test that the wrong origin doesn't have access to the device. EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceId(test_origin1, device_address2)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress2)); EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceId(test_origin2, device_address1)); + allowed_devices_map.GetDeviceId(kTestOrigin2, kDeviceAddress1)); EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceAddress(test_origin1, device_id2)); + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id2)); EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceAddress(test_origin2, device_id1)); + allowed_devices_map.GetDeviceAddress(kTestOrigin2, device_id1)); // Test that we can retrieve the device address/id. EXPECT_EQ(device_id1, - allowed_devices_map.GetDeviceId(test_origin1, device_address1)); + allowed_devices_map.GetDeviceId(kTestOrigin1, kDeviceAddress1)); EXPECT_EQ(device_id2, - allowed_devices_map.GetDeviceId(test_origin2, device_address2)); + allowed_devices_map.GetDeviceId(kTestOrigin2, kDeviceAddress2)); - EXPECT_EQ(device_address1, - allowed_devices_map.GetDeviceAddress(test_origin1, device_id1)); - EXPECT_EQ(device_address2, - allowed_devices_map.GetDeviceAddress(test_origin2, device_id2)); + EXPECT_EQ(kDeviceAddress1, + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id1)); + EXPECT_EQ(kDeviceAddress2, + allowed_devices_map.GetDeviceAddress(kTestOrigin2, device_id2)); } -TEST_F(BluetoothAllowedDevicesMapTest, AddDeviceFromTwoOriginsToMap) { +TEST(BluetoothAllowedDevicesMapTest, AddDeviceFromTwoOriginsToMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id1 = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); const std::string& device_id2 = allowed_devices_map.AddDevice( - test_origin2, device_address1, filters, optional_services); + kTestOrigin2, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); EXPECT_NE(device_id1, device_id2); // Test that the wrong origin doesn't have access to the device. EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceAddress(test_origin1, device_id2)); + allowed_devices_map.GetDeviceAddress(kTestOrigin1, device_id2)); EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceAddress(test_origin2, device_id1)); + allowed_devices_map.GetDeviceAddress(kTestOrigin2, device_id1)); } -TEST_F(BluetoothAllowedDevicesMapTest, AddRemoveAddDeviceToMap) { +TEST(BluetoothAllowedDevicesMapTest, AddRemoveAddDeviceToMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string device_id_first_time = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); - allowed_devices_map.RemoveDevice(test_origin1, device_address1); + allowed_devices_map.RemoveDevice(kTestOrigin1, kDeviceAddress1); const std::string device_id_second_time = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); EXPECT_NE(device_id_first_time, device_id_second_time); } -TEST_F(BluetoothAllowedDevicesMapTest, RemoveDeviceFromMap) { +TEST(BluetoothAllowedDevicesMapTest, RemoveDeviceFromMap) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); - allowed_devices_map.RemoveDevice(test_origin1, device_address1); + allowed_devices_map.RemoveDevice(kTestOrigin1, kDeviceAddress1); EXPECT_EQ(base::EmptyString(), - allowed_devices_map.GetDeviceId(test_origin1, device_id)); + allowed_devices_map.GetDeviceId(kTestOrigin1, device_id)); EXPECT_EQ(base::EmptyString(), allowed_devices_map.GetDeviceAddress( - test_origin1, device_address1)); + kTestOrigin1, kDeviceAddress1)); +} + +TEST(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) { + BluetoothAllowedDevicesMap allowed_devices_map; + + // Setup device. + BluetoothScanFilter scanFilter1; + scanFilter1.services.push_back(kGlucoseUUID); + BluetoothScanFilter scanFilter2; + scanFilter2.services.push_back(kHeartRateUUID); + + std::vector filters; + filters.push_back(scanFilter1); + filters.push_back(scanFilter2); + + std::vector optional_services; + optional_services.push_back(kBatteryServiceUUID); + optional_services.push_back(kHeartRateUUID); + + // Add to map. + const std::string device_id1 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters, optional_services); + + // Access allowed services. + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); + + // Try to access a non-allowed service. + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBloodPressureUUIDString)); + + // Try to access allowed services after removing device. + allowed_devices_map.RemoveDevice(kTestOrigin1, kDeviceAddress1); + + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); + + // Add device back. + const std::string device_id2 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters, kEmptyOptionalServices); + + // Access allowed services. + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kGlucoseUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kHeartRateUUIDString)); + + // Try to access a non-allowed service. + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kBatteryServiceUUIDString)); + + // Try to access services from old device. + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); +} + +TEST(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) { + BluetoothAllowedDevicesMap allowed_devices_map; + + // Setup request for device #1. + BluetoothScanFilter scanFilter1; + scanFilter1.services.push_back(kGlucoseUUID); + std::vector filters1; + filters1.push_back(scanFilter1); + + std::vector optional_services1; + optional_services1.push_back(kHeartRateUUID); + + // Setup request for device #2. + BluetoothScanFilter scanFilter2; + scanFilter2.services.push_back(kBatteryServiceUUID); + std::vector filters2; + filters2.push_back(scanFilter2); + + std::vector optional_services2; + optional_services2.push_back(kBloodPressureUUID); + + // Add devices to map. + const std::string& device_id1 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters1, optional_services1); + const std::string& device_id2 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress2, filters2, optional_services2); + + // Access allowed services. + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kBatteryServiceUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kBloodPressureUUIDString)); + + // Try to access non-allowed services. + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBloodPressureUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kCyclingPowerUUIDString)); + + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kHeartRateUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kCyclingPowerUUIDString)); +} + +TEST(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) { + BluetoothAllowedDevicesMap allowed_devices_map; + // Setup request #1 for device. + BluetoothScanFilter scanFilter1; + scanFilter1.services.push_back(kGlucoseUUID); + std::vector filters1; + filters1.push_back(scanFilter1); + + std::vector optional_services1; + optional_services1.push_back(kHeartRateUUID); + + // Setup request #2 for device. + BluetoothScanFilter scanFilter2; + scanFilter2.services.push_back(kBatteryServiceUUID); + std::vector filters2; + filters2.push_back(scanFilter2); + + std::vector optional_services2; + optional_services2.push_back(kBloodPressureUUID); + + // Add devices to map. + const std::string& device_id1 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters1, optional_services1); + const std::string& device_id2 = allowed_devices_map.AddDevice( + kTestOrigin2, kDeviceAddress1, filters2, optional_services2); + + // Access allowed services. + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id2, kBatteryServiceUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id2, kBloodPressureUUIDString)); + + // Try to access non-allowed services. + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBloodPressureUUIDString)); + + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kHeartRateUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kBatteryServiceUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id2, kBloodPressureUUIDString)); + + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id2, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id2, kHeartRateUUIDString)); + + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id1, kGlucoseUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id1, kHeartRateUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id1, kBatteryServiceUUIDString)); + EXPECT_FALSE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin2, device_id1, kBloodPressureUUIDString)); +} + +TEST(BluetoothAllowedDevicesMapTest, MergeServices) { + BluetoothAllowedDevicesMap allowed_devices_map; + + // Setup first request. + BluetoothScanFilter scanFilter1; + scanFilter1.services.push_back(kGlucoseUUID); + std::vector filters1; + filters1.push_back(scanFilter1); + std::vector optional_services1; + optional_services1.push_back(kBatteryServiceUUID); + + // Add to map. + const std::string device_id1 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters1, optional_services1); + + // Setup second request. + BluetoothScanFilter scanFilter2; + scanFilter2.services.push_back(kHeartRateUUID); + std::vector filters2; + filters2.push_back(scanFilter2); + std::vector optional_services2; + optional_services2.push_back(kBloodPressureUUID); + + // Add to map again. + const std::string device_id2 = allowed_devices_map.AddDevice( + kTestOrigin1, kDeviceAddress1, filters2, optional_services2); + + EXPECT_EQ(device_id1, device_id2); + + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kGlucoseUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBatteryServiceUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kHeartRateUUIDString)); + EXPECT_TRUE(allowed_devices_map.IsOriginAllowedToAccessService( + kTestOrigin1, device_id1, kBloodPressureUUIDString)); } -TEST_F(BluetoothAllowedDevicesMapTest, CorrectIdFormat) { +TEST(BluetoothAllowedDevicesMapTest, CorrectIdFormat) { BluetoothAllowedDevicesMap allowed_devices_map; const std::string& device_id = allowed_devices_map.AddDevice( - test_origin1, device_address1, filters, optional_services); + kTestOrigin1, kDeviceAddress1, kEmptyFilters, kEmptyOptionalServices); EXPECT_TRUE(device_id.size() == 24) << "Expected Lenghth of a 128bit string encoded to Base64."; diff --git a/chromium/content/browser/bluetooth/bluetooth_blacklist.cc b/chromium/content/browser/bluetooth/bluetooth_blacklist.cc new file mode 100644 index 00000000000..c9edc5e3806 --- /dev/null +++ b/chromium/content/browser/bluetooth/bluetooth_blacklist.cc @@ -0,0 +1,185 @@ +// 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 "content/browser/bluetooth/bluetooth_blacklist.h" + +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/string_split.h" +#include "content/common/bluetooth/bluetooth_scan_filter.h" +#include "content/public/browser/content_browser_client.h" + +using device::BluetoothUUID; + +namespace { + +static base::LazyInstance::Leaky g_singleton = + LAZY_INSTANCE_INITIALIZER; + +void RecordUMAParsedNonEmptyString(bool success) { + UMA_HISTOGRAM_BOOLEAN("Bluetooth.Web.Blacklist.ParsedNonEmptyString", + success); +} + +} // namespace + +namespace content { + +BluetoothBlacklist::~BluetoothBlacklist() {} + +// static +BluetoothBlacklist& BluetoothBlacklist::Get() { + return g_singleton.Get(); +} + +void BluetoothBlacklist::Add(const device::BluetoothUUID& uuid, Value value) { + CHECK(uuid.IsValid()); + auto insert_result = blacklisted_uuids_.insert(std::make_pair(uuid, value)); + bool inserted = insert_result.second; + if (!inserted) { + Value& stored = insert_result.first->second; + if (stored != value) + stored = Value::EXCLUDE; + } +} + +void BluetoothBlacklist::Add(base::StringPiece blacklist_string) { + if (blacklist_string.empty()) + return; + base::StringPairs kv_pairs; + bool parsed_values = false; + bool invalid_values = false; + SplitStringIntoKeyValuePairs(blacklist_string, + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs); + for (const auto& pair : kv_pairs) { + BluetoothUUID uuid(pair.first); + if (uuid.IsValid() && pair.second.size() == 1u) { + switch (pair.second[0]) { + case 'e': + Add(uuid, Value::EXCLUDE); + parsed_values = true; + continue; + case 'r': + Add(uuid, Value::EXCLUDE_READS); + parsed_values = true; + continue; + case 'w': + Add(uuid, Value::EXCLUDE_WRITES); + parsed_values = true; + continue; + } + } + invalid_values = true; + } + RecordUMAParsedNonEmptyString(parsed_values && !invalid_values); +} + +bool BluetoothBlacklist::IsExcluded(const BluetoothUUID& uuid) const { + CHECK(uuid.IsValid()); + const auto& it = blacklisted_uuids_.find(uuid); + if (it == blacklisted_uuids_.end()) + return false; + return it->second == Value::EXCLUDE; +} + +bool BluetoothBlacklist::IsExcluded( + const std::vector& filters) { + for (const BluetoothScanFilter& filter : filters) { + for (const BluetoothUUID& service : filter.services) { + if (IsExcluded(service)) { + return true; + } + } + } + return false; +} + +bool BluetoothBlacklist::IsExcludedFromReads(const BluetoothUUID& uuid) const { + CHECK(uuid.IsValid()); + const auto& it = blacklisted_uuids_.find(uuid); + if (it == blacklisted_uuids_.end()) + return false; + return it->second == Value::EXCLUDE || it->second == Value::EXCLUDE_READS; +} + +bool BluetoothBlacklist::IsExcludedFromWrites(const BluetoothUUID& uuid) const { + CHECK(uuid.IsValid()); + const auto& it = blacklisted_uuids_.find(uuid); + if (it == blacklisted_uuids_.end()) + return false; + return it->second == Value::EXCLUDE || it->second == Value::EXCLUDE_WRITES; +} + +void BluetoothBlacklist::RemoveExcludedUuids( + std::vector* uuids) { + auto it = uuids->begin(); + while (it != uuids->end()) { + if (IsExcluded(*it)) { + it = uuids->erase(it); + } else { + it++; + } + } +} + +void BluetoothBlacklist::ResetToDefaultValuesForTest() { + blacklisted_uuids_.clear(); + PopulateWithDefaultValues(); + PopulateWithServerProvidedValues(); +} + +BluetoothBlacklist::BluetoothBlacklist() { + PopulateWithDefaultValues(); + PopulateWithServerProvidedValues(); +} + +void BluetoothBlacklist::PopulateWithDefaultValues() { + blacklisted_uuids_.clear(); + + // Testing from Layout Tests Note: + // + // Random UUIDs for object & exclude permutations that do not exist in the + // standard blacklist are included to facilitate integration testing from + // Layout Tests. Unit tests can dynamically modify the blacklist, but don't + // offer the full integration test to the Web Bluetooth Javascript bindings. + // + // This is done for simplicity as opposed to exposing a testing API that can + // add to the blacklist over time, which would be over engineered. + // + // Remove testing UUIDs if the specified blacklist is updated to include UUIDs + // that match the specific permutations. + DCHECK(BluetoothUUID("00001800-0000-1000-8000-00805f9b34fb") == + BluetoothUUID("1800")); + + // Blacklist UUIDs updated 2016-04-07 from: + // https://github.com/WebBluetoothCG/registries/blob/master/gatt_blacklist.txt + // Short UUIDs are used for readability of this list. + // + // Services: + Add(BluetoothUUID("1812"), Value::EXCLUDE); + Add(BluetoothUUID("00001530-1212-efde-1523-785feabcd123"), Value::EXCLUDE); + Add(BluetoothUUID("f000ffc0-0451-4000-b000-000000000000"), Value::EXCLUDE); + // Characteristics: + Add(BluetoothUUID("2a02"), Value::EXCLUDE_WRITES); + Add(BluetoothUUID("2a03"), Value::EXCLUDE); + Add(BluetoothUUID("2a25"), Value::EXCLUDE); + // Characteristics for Layout Tests: + Add(BluetoothUUID("bad1c9a2-9a5b-4015-8b60-1579bbbf2135"), + Value::EXCLUDE_READS); + // Descriptors: + Add(BluetoothUUID("2902"), Value::EXCLUDE_WRITES); + Add(BluetoothUUID("2903"), Value::EXCLUDE_WRITES); + // Descriptors for Layout Tests: + Add(BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"), Value::EXCLUDE); + Add(BluetoothUUID("bad3ec61-3cc3-4954-9702-7977df514114"), + Value::EXCLUDE_READS); +} + +void BluetoothBlacklist::PopulateWithServerProvidedValues() { + Add(GetContentClient()->browser()->GetWebBluetoothBlacklist()); +} + +} // namespace content diff --git a/chromium/content/browser/bluetooth/bluetooth_blacklist.h b/chromium/content/browser/bluetooth/bluetooth_blacklist.h new file mode 100644 index 00000000000..d363d86cfb9 --- /dev/null +++ b/chromium/content/browser/bluetooth/bluetooth_blacklist.h @@ -0,0 +1,104 @@ +// 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 CONTENT_BROWSER_BLUETOOTH_BLUETOOTH_BLACKLIST_H_ +#define CONTENT_BROWSER_BLUETOOTH_BLUETOOTH_BLACKLIST_H_ + +#include +#include + +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "base/strings/string_piece.h" +#include "content/common/content_export.h" +#include "device/bluetooth/bluetooth_uuid.h" + +namespace content { + +struct BluetoothScanFilter; + +// Implements the Web Bluetooth Blacklist policy as defined in the Web Bluetooth +// specification: +// https://webbluetoothcg.github.io/web-bluetooth/#the-gatt-blacklist +// +// Client code may query UUIDs to determine if they are excluded from use by the +// blacklist. +// +// Singleton access via Get() enforces only one copy of blacklist. +class CONTENT_EXPORT BluetoothBlacklist final { + public: + // Blacklist value terminology from Web Bluetooth specification: + // https://webbluetoothcg.github.io/web-bluetooth/#the-gatt-blacklist + enum class Value { + EXCLUDE, // Implies EXCLUDE_READS and EXCLUDE_WRITES. + EXCLUDE_READS, // Excluded from read operations. + EXCLUDE_WRITES // Excluded from write operations. + }; + + ~BluetoothBlacklist(); + + // Returns a singleton instance of the blacklist. + static BluetoothBlacklist& Get(); + + // Adds a UUID to the blacklist to be excluded from operations, merging with + // any previous value and resulting in the strictest exclusion rule from the + // combination of the two, E.G.: + // Add(uuid, EXCLUDE_READS); + // Add(uuid, EXCLUDE_WRITES); + // IsExcluded(uuid); // true. + // Requires UUID to be valid. + void Add(const device::BluetoothUUID&, Value); + + // Adds UUIDs to the blacklist by parsing a blacklist string and calling + // Add(uuid, value). + // + // The blacklist string format is defined at + // ContentBrowserClient::GetWebBluetoothBlacklist(). + // + // Malformed pairs in the string are ignored, including invalid UUID or + // exclusion values. Duplicate UUIDs follow Add()'s merging rule. + void Add(base::StringPiece blacklist_string); + + // Returns if a UUID is excluded from all operations. UUID must be valid. + bool IsExcluded(const device::BluetoothUUID&) const; + + // Returns if any UUID in a set of filters is excluded from all operations. + // UUID must be valid. + bool IsExcluded(const std::vector&); + + // Returns if a UUID is excluded from read operations. UUID must be valid. + bool IsExcludedFromReads(const device::BluetoothUUID&) const; + + // Returns if a UUID is excluded from write operations. UUID must be valid. + bool IsExcludedFromWrites(const device::BluetoothUUID&) const; + + // Modifies a list of UUIDs, removing any UUIDs with Value::EXCLUDE. + void RemoveExcludedUuids(std::vector*); + + // Size of blacklist. + size_t size() { return blacklisted_uuids_.size(); } + + void ResetToDefaultValuesForTest(); + + private: + // friend LazyInstance to permit access to private constructor. + friend base::DefaultLazyInstanceTraits; + + BluetoothBlacklist(); + + void PopulateWithDefaultValues(); + + // Populates blacklist with values obtained dynamically from a server, able + // to be updated without shipping new executable versions. + void PopulateWithServerProvidedValues(); + + // Map of UUID to blacklisted value. + std::map blacklisted_uuids_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothBlacklist); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BLUETOOTH_BLUETOOTH_BLACKLIST_H_ diff --git a/chromium/content/browser/bluetooth/bluetooth_blacklist_unittest.cc b/chromium/content/browser/bluetooth/bluetooth_blacklist_unittest.cc new file mode 100644 index 00000000000..b0ed9c2c6a1 --- /dev/null +++ b/chromium/content/browser/bluetooth/bluetooth_blacklist_unittest.cc @@ -0,0 +1,398 @@ +// 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 "content/browser/bluetooth/bluetooth_blacklist.h" + +#include "content/common/bluetooth/bluetooth_scan_filter.h" +#include "device/bluetooth/bluetooth_uuid.h" +#include "testing/gtest/include/gtest/gtest.h" + +using device::BluetoothUUID; + +namespace content { + +class BluetoothBlacklistTest : public ::testing::Test { + public: + BluetoothBlacklistTest() : list_(BluetoothBlacklist::Get()) { + // Because BluetoothBlacklist is used via a singleton instance, the data + // must be reset for each test. + list_.ResetToDefaultValuesForTest(); + } + BluetoothBlacklist& list_; +}; + +TEST_F(BluetoothBlacklistTest, NonExcludedUUID) { + BluetoothUUID non_excluded_uuid("00000000-0000-0000-0000-000000000000"); + EXPECT_FALSE(list_.IsExcluded(non_excluded_uuid)); + EXPECT_FALSE(list_.IsExcludedFromReads(non_excluded_uuid)); + EXPECT_FALSE(list_.IsExcludedFromWrites(non_excluded_uuid)); +} + +TEST_F(BluetoothBlacklistTest, ExcludeUUID) { + BluetoothUUID excluded_uuid("eeee"); + list_.Add(excluded_uuid, BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE(list_.IsExcluded(excluded_uuid)); + EXPECT_TRUE(list_.IsExcludedFromReads(excluded_uuid)); + EXPECT_TRUE(list_.IsExcludedFromWrites(excluded_uuid)); +} + +TEST_F(BluetoothBlacklistTest, ExcludeReadsUUID) { + BluetoothUUID exclude_reads_uuid("eeee"); + list_.Add(exclude_reads_uuid, BluetoothBlacklist::Value::EXCLUDE_READS); + EXPECT_FALSE(list_.IsExcluded(exclude_reads_uuid)); + EXPECT_TRUE(list_.IsExcludedFromReads(exclude_reads_uuid)); + EXPECT_FALSE(list_.IsExcludedFromWrites(exclude_reads_uuid)); +} + +TEST_F(BluetoothBlacklistTest, ExcludeWritesUUID) { + BluetoothUUID exclude_writes_uuid("eeee"); + list_.Add(exclude_writes_uuid, BluetoothBlacklist::Value::EXCLUDE_WRITES); + EXPECT_FALSE(list_.IsExcluded(exclude_writes_uuid)); + EXPECT_FALSE(list_.IsExcludedFromReads(exclude_writes_uuid)); + EXPECT_TRUE(list_.IsExcludedFromWrites(exclude_writes_uuid)); +} + +TEST_F(BluetoothBlacklistTest, InvalidUUID) { + BluetoothUUID empty_string_uuid(""); + EXPECT_DEATH_IF_SUPPORTED( + list_.Add(empty_string_uuid, BluetoothBlacklist::Value::EXCLUDE), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcluded(empty_string_uuid), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcludedFromReads(empty_string_uuid), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcludedFromWrites(empty_string_uuid), ""); + + BluetoothUUID invalid_string_uuid("Not a valid UUID string."); + EXPECT_DEATH_IF_SUPPORTED( + list_.Add(invalid_string_uuid, BluetoothBlacklist::Value::EXCLUDE), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcluded(invalid_string_uuid), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcludedFromReads(invalid_string_uuid), ""); + EXPECT_DEATH_IF_SUPPORTED(list_.IsExcludedFromWrites(invalid_string_uuid), + ""); +} + +// Abreviated UUIDs used to create, or test against, the blacklist work +// correctly compared to full UUIDs. +TEST_F(BluetoothBlacklistTest, AbreviatedUUIDs) { + list_.Add(BluetoothUUID("aaaa"), BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE( + list_.IsExcluded(BluetoothUUID("0000aaaa-0000-1000-8000-00805f9b34fb"))); + + list_.Add(BluetoothUUID("0000bbbb-0000-1000-8000-00805f9b34fb"), + BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("bbbb"))); +} + +// Tests permutations of previous values and then Add() with a new value, +// requiring result to be strictest result of the combination. +TEST_F(BluetoothBlacklistTest, Add_MergingExcludeValues) { + list_.Add(BluetoothUUID("ee01"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("ee01"), BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee01"))); + + list_.Add(BluetoothUUID("ee02"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("ee02"), BluetoothBlacklist::Value::EXCLUDE_READS); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee02"))); + + list_.Add(BluetoothUUID("ee03"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("ee03"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee03"))); + + list_.Add(BluetoothUUID("ee04"), BluetoothBlacklist::Value::EXCLUDE_READS); + list_.Add(BluetoothUUID("ee04"), BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee04"))); + + list_.Add(BluetoothUUID("ee05"), BluetoothBlacklist::Value::EXCLUDE_READS); + list_.Add(BluetoothUUID("ee05"), BluetoothBlacklist::Value::EXCLUDE_READS); + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("ee05"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("ee05"))); + + list_.Add(BluetoothUUID("ee06"), BluetoothBlacklist::Value::EXCLUDE_READS); + list_.Add(BluetoothUUID("ee06"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee06"))); + + list_.Add(BluetoothUUID("ee07"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + list_.Add(BluetoothUUID("ee07"), BluetoothBlacklist::Value::EXCLUDE); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee07"))); + + list_.Add(BluetoothUUID("ee08"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + list_.Add(BluetoothUUID("ee08"), BluetoothBlacklist::Value::EXCLUDE_READS); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("ee08"))); + + list_.Add(BluetoothUUID("ee09"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + list_.Add(BluetoothUUID("ee09"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("ee09"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("ee09"))); +} + +// Tests Add() with string that contains many UUID:exclusion value pairs, +// checking that the correct blacklist entries are created for them. +TEST_F(BluetoothBlacklistTest, Add_StringWithValidEntries) { + list_.Add( + "0001:e,0002:r,0003:w, " // Single items. + "0004:r,0004:r, " // Duplicate items. + "0005:r,0005:w, " // Items that merge. + "00000006:e, " // 8 char UUID. + "00000007-0000-1000-8000-00805f9b34fb:e"); + + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0001"))); + + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("0002"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("0002"))); + + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("0003"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("0003"))); + + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("0004"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("0004"))); + + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0005"))); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0006"))); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0007"))); +} + +// Tests Add() with strings that contain no valid UUID:exclusion value. +TEST_F(BluetoothBlacklistTest, Add_StringsWithNoValidEntries) { + size_t previous_list_size = list_.size(); + list_.Add(""); + list_.Add("~!@#$%^&*()-_=+[]{}/*-"); + list_.Add(":"); + list_.Add(","); + list_.Add(",,"); + list_.Add(",:,"); + list_.Add("1234:"); + list_.Add("1234:q"); + list_.Add("1234:E"); + list_.Add("1234:R"); + list_.Add("1234:W"); + list_.Add("1234:ee"); + list_.Add("1234 :e"); + list_.Add("1234: e"); + list_.Add("1:e"); + list_.Add("1:r"); + list_.Add("1:w"); + list_.Add("00001800-0000-1000-8000-00805f9b34fb:ee"); + list_.Add("z0001800-0000-1000-8000-00805f9b34fb:e"); + list_.Add("☯"); + EXPECT_EQ(previous_list_size, list_.size()); +} + +// Tests Add() with strings that contain exactly one valid UUID:exclusion value +// pair, and optionally other issues in the string that are ignored. +TEST_F(BluetoothBlacklistTest, Add_StringsWithOneValidEntry) { + size_t previous_list_size = list_.size(); + list_.Add("0001:e"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0001"))); + + list_.Add("00000002:e"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0002"))); + + list_.Add("00000003-0000-1000-8000-00805f9b34fb:e"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0003"))); + + list_.Add(" 0004:e "); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0004"))); + + list_.Add(", 0005:e ,"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0005"))); + + list_.Add(":, 0006:e ,,no"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0006"))); + + list_.Add("0007:, 0008:e"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0008"))); + + list_.Add("\r\n0009:e\n\r"); + EXPECT_EQ(++previous_list_size, list_.size()); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("0009"))); +} + +TEST_F(BluetoothBlacklistTest, IsExcluded_BluetoothScanFilter_ReturnsFalse) { + list_.Add(BluetoothUUID("eeee"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("ee01"), BluetoothBlacklist::Value::EXCLUDE_READS); + list_.Add(BluetoothUUID("ee02"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + { + std::vector empty_filters; + EXPECT_FALSE(list_.IsExcluded(empty_filters)); + } + { + std::vector single_empty_filter(1); + EXPECT_EQ(0u, single_empty_filter[0].services.size()); + EXPECT_FALSE(list_.IsExcluded(single_empty_filter)); + } + { + std::vector single_non_matching_filter(1); + single_non_matching_filter[0].services.push_back(BluetoothUUID("0000")); + EXPECT_FALSE(list_.IsExcluded(single_non_matching_filter)); + } + { + std::vector multiple_non_matching_filter(2); + multiple_non_matching_filter[0].services.push_back(BluetoothUUID("0000")); + multiple_non_matching_filter[0].services.push_back(BluetoothUUID("ee01")); + multiple_non_matching_filter[1].services.push_back(BluetoothUUID("ee02")); + multiple_non_matching_filter[1].services.push_back(BluetoothUUID("0003")); + EXPECT_FALSE(list_.IsExcluded(multiple_non_matching_filter)); + } +} + +TEST_F(BluetoothBlacklistTest, IsExcluded_BluetoothScanFilter_ReturnsTrue) { + list_.Add(BluetoothUUID("eeee"), BluetoothBlacklist::Value::EXCLUDE); + { + std::vector single_matching_filter(1); + single_matching_filter[0].services.push_back(BluetoothUUID("eeee")); + EXPECT_TRUE(list_.IsExcluded(single_matching_filter)); + } + { + std::vector first_matching_filter(2); + first_matching_filter[0].services.push_back(BluetoothUUID("eeee")); + first_matching_filter[0].services.push_back(BluetoothUUID("0001")); + first_matching_filter[1].services.push_back(BluetoothUUID("0002")); + first_matching_filter[1].services.push_back(BluetoothUUID("0003")); + EXPECT_TRUE(list_.IsExcluded(first_matching_filter)); + } + { + std::vector last_matching_filter(2); + last_matching_filter[0].services.push_back(BluetoothUUID("0001")); + last_matching_filter[0].services.push_back(BluetoothUUID("0001")); + last_matching_filter[1].services.push_back(BluetoothUUID("0002")); + last_matching_filter[1].services.push_back(BluetoothUUID("eeee")); + EXPECT_TRUE(list_.IsExcluded(last_matching_filter)); + } + { + std::vector multiple_matching_filter(2); + multiple_matching_filter[0].services.push_back(BluetoothUUID("eeee")); + multiple_matching_filter[0].services.push_back(BluetoothUUID("eeee")); + multiple_matching_filter[1].services.push_back(BluetoothUUID("eeee")); + multiple_matching_filter[1].services.push_back(BluetoothUUID("eeee")); + EXPECT_TRUE(list_.IsExcluded(multiple_matching_filter)); + } +} + +TEST_F(BluetoothBlacklistTest, RemoveExcludedUuids_NonMatching) { + list_.Add(BluetoothUUID("eeee"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("ee01"), BluetoothBlacklist::Value::EXCLUDE_READS); + list_.Add(BluetoothUUID("ee02"), BluetoothBlacklist::Value::EXCLUDE_WRITES); + { + std::vector empty; + std::vector expected_empty; + list_.RemoveExcludedUuids(&empty); + EXPECT_EQ(expected_empty, empty); + } + { + std::vector single_non_matching; + single_non_matching.push_back(BluetoothUUID("0000")); + + std::vector expected_copy(single_non_matching); + + list_.RemoveExcludedUuids(&single_non_matching); + EXPECT_EQ(expected_copy, single_non_matching); + } + { + std::vector multiple_non_matching; + multiple_non_matching.push_back(BluetoothUUID("0000")); + multiple_non_matching.push_back(BluetoothUUID("ee01")); + multiple_non_matching.push_back(BluetoothUUID("ee02")); + multiple_non_matching.push_back(BluetoothUUID("0003")); + + std::vector expected_copy(multiple_non_matching); + + list_.RemoveExcludedUuids(&multiple_non_matching); + EXPECT_EQ(expected_copy, multiple_non_matching); + } +} + +TEST_F(BluetoothBlacklistTest, RemoveExcludedUuids_Matching) { + list_.Add(BluetoothUUID("eeee"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("eee2"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("eee3"), BluetoothBlacklist::Value::EXCLUDE); + list_.Add(BluetoothUUID("eee4"), BluetoothBlacklist::Value::EXCLUDE); + { + std::vector single_matching; + single_matching.push_back(BluetoothUUID("eeee")); + + std::vector expected_empty; + + list_.RemoveExcludedUuids(&single_matching); + EXPECT_EQ(expected_empty, single_matching); + } + { + std::vector single_matching_of_many; + single_matching_of_many.push_back(BluetoothUUID("0000")); + single_matching_of_many.push_back(BluetoothUUID("eeee")); + single_matching_of_many.push_back(BluetoothUUID("0001")); + + std::vector expected; + expected.push_back(BluetoothUUID("0000")); + expected.push_back(BluetoothUUID("0001")); + + list_.RemoveExcludedUuids(&single_matching_of_many); + EXPECT_EQ(expected, single_matching_of_many); + } + { + std::vector all_matching_of_many; + all_matching_of_many.push_back(BluetoothUUID("eee2")); + all_matching_of_many.push_back(BluetoothUUID("eee4")); + all_matching_of_many.push_back(BluetoothUUID("eee3")); + all_matching_of_many.push_back(BluetoothUUID("eeee")); + + std::vector expected_empty; + + list_.RemoveExcludedUuids(&all_matching_of_many); + EXPECT_EQ(expected_empty, all_matching_of_many); + } +} + +TEST_F(BluetoothBlacklistTest, VerifyDefaultBlacklistSize) { + // When adding items to the blacklist the new values should be added in the + // tests below for each exclusion type. + EXPECT_EQ(11u, list_.size()); +} + +TEST_F(BluetoothBlacklistTest, VerifyDefaultExcludeList) { + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("1800"))); + EXPECT_FALSE(list_.IsExcluded(BluetoothUUID("1801"))); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("1812"))); + EXPECT_TRUE( + list_.IsExcluded(BluetoothUUID("00001530-1212-efde-1523-785feabcd123"))); + EXPECT_TRUE( + list_.IsExcluded(BluetoothUUID("f000ffc0-0451-4000-b000-000000000000"))); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("2a03"))); + EXPECT_TRUE(list_.IsExcluded(BluetoothUUID("2a25"))); + EXPECT_TRUE( + list_.IsExcluded(BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"))); +} + +TEST_F(BluetoothBlacklistTest, VerifyDefaultExcludeReadList) { + EXPECT_FALSE(list_.IsExcludedFromReads(BluetoothUUID("1800"))); + EXPECT_FALSE(list_.IsExcludedFromReads(BluetoothUUID("1801"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("1812"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("2a03"))); + EXPECT_TRUE(list_.IsExcludedFromReads(BluetoothUUID("2a25"))); + EXPECT_TRUE(list_.IsExcludedFromReads( + BluetoothUUID("bad1c9a2-9a5b-4015-8b60-1579bbbf2135"))); + EXPECT_TRUE(list_.IsExcludedFromReads( + BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"))); + EXPECT_TRUE(list_.IsExcludedFromReads( + BluetoothUUID("bad3ec61-3cc3-4954-9702-7977df514114"))); +} + +TEST_F(BluetoothBlacklistTest, VerifyDefaultExcludeWriteList) { + EXPECT_FALSE(list_.IsExcludedFromWrites(BluetoothUUID("1800"))); + EXPECT_FALSE(list_.IsExcludedFromWrites(BluetoothUUID("1801"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("1812"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("2a02"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("2a03"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("2a25"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("2902"))); + EXPECT_TRUE(list_.IsExcludedFromWrites(BluetoothUUID("2903"))); + EXPECT_TRUE(list_.IsExcludedFromWrites( + BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"))); +} + +} // namespace content diff --git a/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.cc index 85bf81e0003..f8444caec79 100644 --- a/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.cc +++ b/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.cc @@ -20,10 +20,11 @@ #include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" #include "content/browser/bad_message.h" -#include "content/browser/bluetooth/bluetooth_metrics.h" +#include "content/browser/bluetooth/bluetooth_blacklist.h" #include "content/browser/bluetooth/first_device_bluetooth_chooser.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/common/bluetooth/bluetooth_messages.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/content_browser_client.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "device/bluetooth/bluetooth_adapter.h" @@ -116,94 +117,94 @@ WebBluetoothError TranslateConnectError( switch (error_code) { case device::BluetoothDevice::ERROR_UNKNOWN: RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNKNOWN); - return WebBluetoothError::ConnectUnknownError; + return WebBluetoothError::CONNECT_UNKNOWN_ERROR; case device::BluetoothDevice::ERROR_INPROGRESS: RecordConnectGATTOutcome(UMAConnectGATTOutcome::IN_PROGRESS); - return WebBluetoothError::ConnectAlreadyInProgress; + return WebBluetoothError::CONNECT_ALREADY_IN_PROGRESS; case device::BluetoothDevice::ERROR_FAILED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::FAILED); - return WebBluetoothError::ConnectUnknownFailure; + return WebBluetoothError::CONNECT_UNKNOWN_FAILURE; case device::BluetoothDevice::ERROR_AUTH_FAILED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_FAILED); - return WebBluetoothError::ConnectAuthFailed; + return WebBluetoothError::CONNECT_AUTH_FAILED; case device::BluetoothDevice::ERROR_AUTH_CANCELED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_CANCELED); - return WebBluetoothError::ConnectAuthCanceled; + return WebBluetoothError::CONNECT_AUTH_CANCELED; case device::BluetoothDevice::ERROR_AUTH_REJECTED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_REJECTED); - return WebBluetoothError::ConnectAuthRejected; + return WebBluetoothError::CONNECT_AUTH_REJECTED; case device::BluetoothDevice::ERROR_AUTH_TIMEOUT: RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_TIMEOUT); - return WebBluetoothError::ConnectAuthTimeout; + return WebBluetoothError::CONNECT_AUTH_TIMEOUT; case device::BluetoothDevice::ERROR_UNSUPPORTED_DEVICE: RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNSUPPORTED_DEVICE); - return WebBluetoothError::ConnectUnsupportedDevice; + return WebBluetoothError::CONNECT_UNSUPPORTED_DEVICE; case device::BluetoothDevice::ERROR_ATTRIBUTE_LENGTH_INVALID: RecordConnectGATTOutcome(UMAConnectGATTOutcome::ATTRIBUTE_LENGTH_INVALID); - return WebBluetoothError::ConnectAttributeLengthInvalid; + return WebBluetoothError::CONNECT_ATTRIBUTE_LENGTH_INVALID; case device::BluetoothDevice::ERROR_CONNECTION_CONGESTED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::CONNECTION_CONGESTED); - return WebBluetoothError::ConnectConnectionCongested; + return WebBluetoothError::CONNECT_CONNECTION_CONGESTED; case device::BluetoothDevice::ERROR_INSUFFICIENT_ENCRYPTION: RecordConnectGATTOutcome(UMAConnectGATTOutcome::INSUFFICIENT_ENCRYPTION); - return WebBluetoothError::ConnectInsufficientEncryption; + return WebBluetoothError::CONNECT_INSUFFICIENT_ENCRYPTION; case device::BluetoothDevice::ERROR_OFFSET_INVALID: RecordConnectGATTOutcome(UMAConnectGATTOutcome::OFFSET_INVALID); - return WebBluetoothError::ConnectOffsetInvalid; + return WebBluetoothError::CONNECT_OFFSET_INVALID; case device::BluetoothDevice::ERROR_READ_NOT_PERMITTED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::READ_NOT_PERMITTED); - return WebBluetoothError::ConnectReadNotPermitted; + return WebBluetoothError::CONNECT_READ_NOT_PERMITTED; case device::BluetoothDevice::ERROR_REQUEST_NOT_SUPPORTED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::REQUEST_NOT_SUPPORTED); - return WebBluetoothError::ConnectRequestNotSupported; + return WebBluetoothError::CONNECT_REQUEST_NOT_SUPPORTED; case device::BluetoothDevice::ERROR_WRITE_NOT_PERMITTED: RecordConnectGATTOutcome(UMAConnectGATTOutcome::WRITE_NOT_PERMITTED); - return WebBluetoothError::ConnectWriteNotPermitted; + return WebBluetoothError::CONNECT_WRITE_NOT_PERMITTED; case device::BluetoothDevice::NUM_CONNECT_ERROR_CODES: NOTREACHED(); - return WebBluetoothError::UntranslatedConnectErrorCode; + return WebBluetoothError::UNTRANSLATED_CONNECT_ERROR_CODE; } NOTREACHED(); - return WebBluetoothError::UntranslatedConnectErrorCode; + return WebBluetoothError::UNTRANSLATED_CONNECT_ERROR_CODE; } -blink::WebBluetoothError TranslateGATTError( +WebBluetoothError TranslateGATTError( BluetoothGattService::GattErrorCode error_code, UMAGATTOperation operation) { switch (error_code) { case BluetoothGattService::GATT_ERROR_UNKNOWN: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::UNKNOWN); - return blink::WebBluetoothError::GATTUnknownError; + return blink::WebBluetoothError::GATT_UNKNOWN_ERROR; case BluetoothGattService::GATT_ERROR_FAILED: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::FAILED); - return blink::WebBluetoothError::GATTUnknownFailure; + return blink::WebBluetoothError::GATT_UNKNOWN_FAILURE; case BluetoothGattService::GATT_ERROR_IN_PROGRESS: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::IN_PROGRESS); - return blink::WebBluetoothError::GATTOperationInProgress; + return blink::WebBluetoothError::GATT_OPERATION_IN_PROGRESS; case BluetoothGattService::GATT_ERROR_INVALID_LENGTH: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::INVALID_LENGTH); - return blink::WebBluetoothError::GATTInvalidAttributeLength; + return blink::WebBluetoothError::GATT_INVALID_ATTRIBUTE_LENGTH; case BluetoothGattService::GATT_ERROR_NOT_PERMITTED: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::NOT_PERMITTED); - return blink::WebBluetoothError::GATTNotPermitted; + return blink::WebBluetoothError::GATT_NOT_PERMITTED; case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::NOT_AUTHORIZED); - return blink::WebBluetoothError::GATTNotAuthorized; + return blink::WebBluetoothError::GATT_NOT_AUTHORIZED; case BluetoothGattService::GATT_ERROR_NOT_PAIRED: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::NOT_PAIRED); - return blink::WebBluetoothError::GATTNotPaired; + return blink::WebBluetoothError::GATT_NOT_PAIRED; case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED: RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::NOT_SUPPORTED); - return blink::WebBluetoothError::GATTNotSupported; + return blink::WebBluetoothError::GATT_NOT_SUPPORTED; } NOTREACHED(); - return blink::WebBluetoothError::GATTUntranslatedErrorCode; + return blink::WebBluetoothError::GATT_UNTRANSLATED_ERROR_CODE; } void StopDiscoverySession( @@ -232,6 +233,34 @@ std::vector GetPrimaryServicesByUUID( return services; } +UMARequestDeviceOutcome OutcomeFromChooserEvent(BluetoothChooser::Event event) { + switch (event) { + case BluetoothChooser::Event::DENIED_PERMISSION: + return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION; + case BluetoothChooser::Event::CANCELLED: + return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED; + case BluetoothChooser::Event::SHOW_OVERVIEW_HELP: + return UMARequestDeviceOutcome::BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP: + return UMARequestDeviceOutcome::ADAPTER_OFF_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP: + return UMARequestDeviceOutcome::NEED_LOCATION_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SELECTED: + // We can't know if we are going to send a success message yet because + // the device could have vanished. This event should be histogramed + // manually after checking if the device is still around. + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; + case BluetoothChooser::Event::RESCAN: + // Rescanning doesn't result in a IPC message for the request being sent + // so no need to histogram it. + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; + } + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; +} + } // namespace BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id) @@ -251,14 +280,11 @@ BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id) weak_ptr_factory_(this) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + connected_devices_map_.reset(new ConnectedDevicesMap(render_process_id)); + // Bind all future weak pointers to the UI thread. weak_ptr_on_ui_thread_ = weak_ptr_factory_.GetWeakPtr(); weak_ptr_on_ui_thread_.get(); // Associates with UI thread. - - if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) - BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothDispatcherHost::set_adapter, - weak_ptr_factory_.GetWeakPtr())); } void BluetoothDispatcherHost::OnDestruct() const { @@ -278,11 +304,13 @@ bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost, message) IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice, OnRequestDevice) - IPC_MESSAGE_HANDLER(BluetoothHostMsg_ConnectGATT, OnConnectGATT) + IPC_MESSAGE_HANDLER(BluetoothHostMsg_GATTServerConnect, OnGATTServerConnect) + IPC_MESSAGE_HANDLER(BluetoothHostMsg_GATTServerDisconnect, + OnGATTServerDisconnect) IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetPrimaryService, OnGetPrimaryService) IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetCharacteristic, OnGetCharacteristic) + IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetCharacteristics, OnGetCharacteristics) IPC_MESSAGE_HANDLER(BluetoothHostMsg_ReadValue, OnReadValue) - IPC_MESSAGE_HANDLER(BluetoothHostMsg_WriteValue, OnWriteValue) IPC_MESSAGE_HANDLER(BluetoothHostMsg_StartNotifications, OnStartNotifications) IPC_MESSAGE_HANDLER(BluetoothHostMsg_StopNotifications, OnStopNotifications) IPC_MESSAGE_HANDLER(BluetoothHostMsg_RegisterCharacteristic, @@ -321,7 +349,8 @@ void BluetoothDispatcherHost::SetBluetoothAdapterForTesting( characteristic_to_service_.clear(); characteristic_id_to_notify_session_.clear(); active_characteristic_threads_.clear(); - connections_.clear(); + connected_devices_map_.reset(new ConnectedDevicesMap(render_process_id_)); + allowed_devices_map_ = BluetoothAllowedDevicesMap(); } set_adapter(std::move(mock_adapter)); @@ -339,11 +368,13 @@ struct BluetoothDispatcherHost::RequestDeviceSession { public: RequestDeviceSession(int thread_id, int request_id, + int frame_routing_id, url::Origin origin, const std::vector& filters, const std::vector& optional_services) : thread_id(thread_id), request_id(request_id), + frame_routing_id(frame_routing_id), origin(origin), filters(filters), optional_services(optional_services) {} @@ -370,6 +401,7 @@ struct BluetoothDispatcherHost::RequestDeviceSession { const int thread_id; const int request_id; + const int frame_routing_id; const url::Origin origin; const std::vector filters; const std::vector optional_services; @@ -377,40 +409,31 @@ struct BluetoothDispatcherHost::RequestDeviceSession { scoped_ptr discovery_session; }; -struct BluetoothDispatcherHost::CacheQueryResult { - CacheQueryResult() - : device(nullptr), - service(nullptr), - characteristic(nullptr), - outcome(CacheQueryOutcome::SUCCESS) {} - CacheQueryResult(CacheQueryOutcome outcome) - : device(nullptr), - service(nullptr), - characteristic(nullptr), - outcome(outcome) {} - ~CacheQueryResult() {} - WebBluetoothError GetWebError() const { - switch (outcome) { - case CacheQueryOutcome::SUCCESS: - case CacheQueryOutcome::BAD_RENDERER: - NOTREACHED(); - return WebBluetoothError::DeviceNoLongerInRange; - case CacheQueryOutcome::NO_DEVICE: - return WebBluetoothError::DeviceNoLongerInRange; - case CacheQueryOutcome::NO_SERVICE: - return WebBluetoothError::ServiceNoLongerExists; - case CacheQueryOutcome::NO_CHARACTERISTIC: - return WebBluetoothError::CharacteristicNoLongerExists; - } - NOTREACHED(); - return WebBluetoothError::DeviceNoLongerInRange; - } +BluetoothDispatcherHost::CacheQueryResult::CacheQueryResult() {} - device::BluetoothDevice* device; - device::BluetoothGattService* service; - device::BluetoothGattCharacteristic* characteristic; - CacheQueryOutcome outcome; -}; +BluetoothDispatcherHost::CacheQueryResult::CacheQueryResult( + CacheQueryOutcome outcome) + : outcome(outcome) {} + +BluetoothDispatcherHost::CacheQueryResult::~CacheQueryResult() {} + +WebBluetoothError BluetoothDispatcherHost::CacheQueryResult::GetWebError() + const { + switch (outcome) { + case CacheQueryOutcome::SUCCESS: + case CacheQueryOutcome::BAD_RENDERER: + NOTREACHED(); + return WebBluetoothError::DEVICE_NO_LONGER_IN_RANGE; + case CacheQueryOutcome::NO_DEVICE: + return WebBluetoothError::DEVICE_NO_LONGER_IN_RANGE; + case CacheQueryOutcome::NO_SERVICE: + return WebBluetoothError::SERVICE_NO_LONGER_EXISTS; + case CacheQueryOutcome::NO_CHARACTERISTIC: + return WebBluetoothError::CHARACTERISTIC_NO_LONGER_EXISTS; + } + NOTREACHED(); + return WebBluetoothError::DEVICE_NO_LONGER_IN_RANGE; +} struct BluetoothDispatcherHost::PrimaryServicesRequest { enum CallingFunction { GET_PRIMARY_SERVICE, GET_PRIMARY_SERVICES }; @@ -431,10 +454,74 @@ struct BluetoothDispatcherHost::PrimaryServicesRequest { CallingFunction func; }; +BluetoothDispatcherHost::ConnectedDevicesMap::ConnectedDevicesMap( + int render_process_id) + : render_process_id_(render_process_id) {} + +BluetoothDispatcherHost::ConnectedDevicesMap::~ConnectedDevicesMap() { + for (auto frame_id_device_id : frame_ids_device_ids_) { + DecrementBluetoothConnectedDeviceCount(frame_id_device_id.first); + } +} + +bool BluetoothDispatcherHost::ConnectedDevicesMap::HasActiveConnection( + const std::string& device_id) { + auto connection_iter = device_id_to_connection_map_.find(device_id); + if (connection_iter != device_id_to_connection_map_.end()) { + return connection_iter->second->IsConnected(); + } + return false; +} + +void BluetoothDispatcherHost::ConnectedDevicesMap::InsertOrReplace( + int frame_routing_id, + const std::string& device_id, + scoped_ptr connection) { + auto connection_iter = device_id_to_connection_map_.find(device_id); + if (connection_iter == device_id_to_connection_map_.end()) { + IncrementBluetoothConnectedDeviceCount(frame_routing_id); + frame_ids_device_ids_.insert(std::make_pair(frame_routing_id, device_id)); + } else { + device_id_to_connection_map_.erase(connection_iter); + } + device_id_to_connection_map_[device_id] = std::move(connection); +} + +void BluetoothDispatcherHost::ConnectedDevicesMap::Remove( + int frame_routing_id, + const std::string& device_id) { + if (device_id_to_connection_map_.erase(device_id)) { + VLOG(1) << "Disconnecting device: " << device_id; + DecrementBluetoothConnectedDeviceCount(frame_routing_id); + frame_ids_device_ids_.erase(std::make_pair(frame_routing_id, device_id)); + } +} + +void BluetoothDispatcherHost::ConnectedDevicesMap:: + IncrementBluetoothConnectedDeviceCount(int frame_routing_id) { + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id); + WebContentsImpl* web_contents = static_cast( + WebContents::FromRenderFrameHost(render_frame_host)); + if (web_contents) { + web_contents->IncrementBluetoothConnectedDeviceCount(); + } +} + +void BluetoothDispatcherHost::ConnectedDevicesMap:: + DecrementBluetoothConnectedDeviceCount(int frame_routing_id) { + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id); + WebContentsImpl* web_contents = static_cast( + WebContents::FromRenderFrameHost(render_frame_host)); + if (web_contents) { + web_contents->DecrementBluetoothConnectedDeviceCount(); + } +} + void BluetoothDispatcherHost::set_adapter( scoped_refptr adapter) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - connections_.clear(); if (adapter_.get()) adapter_->RemoveObserver(this); adapter_ = adapter; @@ -488,9 +575,21 @@ void BluetoothDispatcherHost::AdapterPoweredChanged( &request_device_sessions_); !iter.IsAtEnd(); iter.Advance()) { RequestDeviceSession* session = iter.GetCurrentValue(); + + // Stop ongoing discovery session if power is off. + if (!powered && session->discovery_session) { + StopDiscoverySession(std::move(session->discovery_session)); + } + if (session->chooser) session->chooser->SetAdapterPresence(presence); } + + // Stop the timer so that we don't change the state of the chooser + // when timer expires. + if (!powered) { + discovery_session_timer_.Stop(); + } } void BluetoothDispatcherHost::DeviceAdded(device::BluetoothAdapter* adapter, @@ -548,7 +647,7 @@ void BluetoothDispatcherHost::GattServicesDiscovered( UMAGetPrimaryServiceOutcome::NOT_FOUND); Send(new BluetoothMsg_GetPrimaryServiceError( request.thread_id, request.request_id, - WebBluetoothError::ServiceNotFound)); + WebBluetoothError::SERVICE_NOT_FOUND)); } break; case PrimaryServicesRequest::GET_PRIMARY_SERVICES: @@ -606,118 +705,29 @@ void BluetoothDispatcherHost::OnRequestDevice( RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::REQUEST_DEVICE); RecordRequestDeviceArguments(filters, optional_services); - VLOG(1) << "requestDevice called with the following filters: "; - for (const BluetoothScanFilter& filter : filters) { - VLOG(1) << "Name: " << filter.name; - VLOG(1) << "Name Prefix: " << filter.namePrefix; - VLOG(1) << "Services:"; - VLOG(1) << "\t["; - for (const BluetoothUUID& service : filter.services) - VLOG(1) << "\t\t" << service.value(); - VLOG(1) << "\t]"; - } - - VLOG(1) << "requestDevice called with the following optional services: "; - for (const BluetoothUUID& service : optional_services) - VLOG(1) << "\t" << service.value(); - - RenderFrameHostImpl* render_frame_host = - RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id); - - if (!render_frame_host) { - DLOG(WARNING) - << "Got a requestDevice IPC without a matching RenderFrameHost: " - << render_process_id_ << ", " << frame_routing_id; - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME); - Send(new BluetoothMsg_RequestDeviceError( - thread_id, request_id, WebBluetoothError::RequestDeviceWithoutFrame)); - return; - } - - if (!adapter_) { - VLOG(1) << "No BluetoothAdapter. Can't serve requestDevice."; + if (!adapter_.get()) { + if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { + BluetoothAdapterFactory::GetAdapter(base::Bind( + &BluetoothDispatcherHost::OnGetAdapter, weak_ptr_on_ui_thread_, + base::Bind(&BluetoothDispatcherHost::OnRequestDeviceImpl, + weak_ptr_on_ui_thread_, thread_id, request_id, + frame_routing_id, filters, optional_services))); + return; + } RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER); Send(new BluetoothMsg_RequestDeviceError( - thread_id, request_id, WebBluetoothError::NoBluetoothAdapter)); - return; - } - - if (!adapter_->IsPresent()) { - VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice."; - RecordRequestDeviceOutcome( - UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT); - Send(new BluetoothMsg_RequestDeviceError( - thread_id, request_id, WebBluetoothError::NoBluetoothAdapter)); + thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER)); return; } - - // The renderer should never send empty filters. - if (HasEmptyOrInvalidFilter(filters)) { - bad_message::ReceivedBadMessage(this, - bad_message::BDH_EMPTY_OR_INVALID_FILTERS); - return; - } - - // Create storage for the information that backs the chooser, and show the - // chooser. - RequestDeviceSession* const session = new RequestDeviceSession( - thread_id, request_id, render_frame_host->GetLastCommittedOrigin(), - filters, optional_services); - int chooser_id = request_device_sessions_.Add(session); - - BluetoothChooser::EventHandler chooser_event_handler = - base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent, - weak_ptr_on_ui_thread_, chooser_id); - if (WebContents* web_contents = - WebContents::FromRenderFrameHost(render_frame_host)) { - if (WebContentsDelegate* delegate = web_contents->GetDelegate()) { - session->chooser = delegate->RunBluetoothChooser( - web_contents, chooser_event_handler, - // TODO(ortuno): Replace with GetLastCommittedOrigin. - // http://crbug.com/577451 - render_frame_host->GetLastCommittedURL().GetOrigin()); - } - } - if (!session->chooser) { - LOG(WARNING) - << "No Bluetooth chooser implementation; falling back to first device."; - session->chooser.reset( - new FirstDeviceBluetoothChooser(chooser_event_handler)); - } - - if (!session->chooser->CanAskForScanningPermission()) { - VLOG(1) << "Closing immediately because Chooser cannot obtain permission."; - OnBluetoothChooserEvent(chooser_id, - BluetoothChooser::Event::DENIED_PERMISSION, ""); - return; - } - - // Populate the initial list of devices. - VLOG(1) << "Populating " << adapter_->GetDevices().size() - << " devices in chooser " << chooser_id; - for (const device::BluetoothDevice* device : adapter_->GetDevices()) { - VLOG(1) << "\t" << device->GetAddress(); - session->AddFilteredDevice(*device); - } - - if (!session->chooser) { - // If the dialog's closing, no need to do any of the rest of this. - return; - } - - if (!adapter_->IsPowered()) { - session->chooser->SetAdapterPresence( - BluetoothChooser::AdapterPresence::POWERED_OFF); - return; - } - - StartDeviceDiscovery(session, chooser_id); + OnRequestDeviceImpl(thread_id, request_id, frame_routing_id, filters, + optional_services); } -void BluetoothDispatcherHost::OnConnectGATT(int thread_id, - int request_id, - int frame_routing_id, - const std::string& device_id) { +void BluetoothDispatcherHost::OnGATTServerConnect( + int thread_id, + int request_id, + int frame_routing_id, + const std::string& device_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::CONNECT_GATT); const base::TimeTicks start_time = base::TimeTicks::Now(); @@ -727,20 +737,53 @@ void BluetoothDispatcherHost::OnConnectGATT(int thread_id, if (query_result.outcome != CacheQueryOutcome::SUCCESS) { RecordConnectGATTOutcome(query_result.outcome); - Send(new BluetoothMsg_ConnectGATTError(thread_id, request_id, - query_result.GetWebError())); + Send(new BluetoothMsg_GATTServerConnectError(thread_id, request_id, + query_result.GetWebError())); + return; + } + + // If we are already connected no need to connect again. + if (connected_devices_map_->HasActiveConnection(device_id)) { + VLOG(1) << "Already connected."; + Send(new BluetoothMsg_GATTServerConnectSuccess(thread_id, request_id)); return; } query_result.device->CreateGattConnection( base::Bind(&BluetoothDispatcherHost::OnGATTConnectionCreated, - weak_ptr_on_ui_thread_, thread_id, request_id, device_id, - start_time), + weak_ptr_on_ui_thread_, thread_id, request_id, + frame_routing_id, device_id, start_time), base::Bind(&BluetoothDispatcherHost::OnCreateGATTConnectionError, weak_ptr_on_ui_thread_, thread_id, request_id, device_id, start_time)); } +void BluetoothDispatcherHost::OnGATTServerDisconnect( + int thread_id, + int frame_routing_id, + const std::string& device_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + RecordWebBluetoothFunctionCall( + UMAWebBluetoothFunction::REMOTE_GATT_SERVER_DISCONNECT); + + // Frames can send a disconnect request after they've started navigating, + // making calls to GetLastCommitted origin invalid. Because we still need + // to disconnect the device, otherwise we would leave users with no other + // option to disconnect than closing the tab, we purposefully don't + // check if the frame has permission to interact with the device. + + // The last BluetoothGattConnection for a device closes the connection when + // it's destroyed. + + // This only catches disconnections from the renderer. If the device + // disconnects by itself, or the renderer frame has been deleted + // due to navigation, we will not hide the indicator. + // TODO(ortuno): Once we move to Frame and Mojo we will be able + // to observe the frame's lifetime and hide the indicator when necessary. + // http://crbug.com/508771 + connected_devices_map_->Remove(frame_routing_id, device_id); +} + void BluetoothDispatcherHost::OnGetPrimaryService( int thread_id, int request_id, @@ -751,8 +794,13 @@ void BluetoothDispatcherHost::OnGetPrimaryService( RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_PRIMARY_SERVICE); RecordGetPrimaryServiceService(BluetoothUUID(service_uuid)); - // TODO(ortuno): Check if service_uuid is in "allowed services" - // https://crbug.com/493460 + if (!allowed_devices_map_.IsOriginAllowedToAccessService( + GetOrigin(frame_routing_id), device_id, service_uuid)) { + Send(new BluetoothMsg_GetPrimaryServiceError( + thread_id, request_id, + WebBluetoothError::NOT_ALLOWED_TO_ACCESS_SERVICE)); + return; + } const CacheQueryResult query_result = QueryCacheForDevice(GetOrigin(frame_routing_id), device_id); @@ -792,7 +840,7 @@ void BluetoothDispatcherHost::OnGetPrimaryService( VLOG(1) << "Service not found in device."; RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND); Send(new BluetoothMsg_GetPrimaryServiceError( - thread_id, request_id, WebBluetoothError::ServiceNotFound)); + thread_id, request_id, WebBluetoothError::SERVICE_NOT_FOUND)); return; } @@ -814,6 +862,16 @@ void BluetoothDispatcherHost::OnGetCharacteristic( RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_CHARACTERISTIC); RecordGetCharacteristicCharacteristic(characteristic_uuid); + // Check Blacklist for characteristic_uuid. + if (BluetoothBlacklist::Get().IsExcluded( + BluetoothUUID(characteristic_uuid))) { + RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::BLACKLISTED); + Send(new BluetoothMsg_GetCharacteristicError( + thread_id, request_id, + WebBluetoothError::BLACKLISTED_CHARACTERISTIC_UUID)); + return; + } + const CacheQueryResult query_result = QueryCacheForService(GetOrigin(frame_routing_id), service_instance_id); @@ -852,59 +910,98 @@ void BluetoothDispatcherHost::OnGetCharacteristic( } RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NOT_FOUND); Send(new BluetoothMsg_GetCharacteristicError( - thread_id, request_id, WebBluetoothError::CharacteristicNotFound)); + thread_id, request_id, WebBluetoothError::CHARACTERISTIC_NOT_FOUND)); } -void BluetoothDispatcherHost::OnReadValue( +void BluetoothDispatcherHost::OnGetCharacteristics( int thread_id, int request_id, int frame_routing_id, - const std::string& characteristic_instance_id) { + const std::string& service_instance_id, + const std::string& characteristics_uuid) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RecordWebBluetoothFunctionCall( - UMAWebBluetoothFunction::CHARACTERISTIC_READ_VALUE); + UMAWebBluetoothFunction::SERVICE_GET_CHARACTERISTICS); + RecordGetCharacteristicsCharacteristic(characteristics_uuid); + + // Check Blacklist for characteristics_uuid. + if (!characteristics_uuid.empty() && + BluetoothBlacklist::Get().IsExcluded( + BluetoothUUID(characteristics_uuid))) { + RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome::BLACKLISTED); + Send(new BluetoothMsg_GetCharacteristicsError( + thread_id, request_id, + WebBluetoothError::BLACKLISTED_CHARACTERISTIC_UUID)); + return; + } - const CacheQueryResult query_result = QueryCacheForCharacteristic( - GetOrigin(frame_routing_id), characteristic_instance_id); + const CacheQueryResult query_result = + QueryCacheForService(GetOrigin(frame_routing_id), service_instance_id); if (query_result.outcome == CacheQueryOutcome::BAD_RENDERER) { return; } if (query_result.outcome != CacheQueryOutcome::SUCCESS) { - RecordCharacteristicReadValueOutcome(query_result.outcome); - Send(new BluetoothMsg_ReadCharacteristicValueError( - thread_id, request_id, query_result.GetWebError())); + RecordGetCharacteristicsOutcome(query_result.outcome); + Send(new BluetoothMsg_GetCharacteristicsError(thread_id, request_id, + query_result.GetWebError())); return; } - query_result.characteristic->ReadRemoteCharacteristic( - base::Bind(&BluetoothDispatcherHost::OnCharacteristicValueRead, - weak_ptr_on_ui_thread_, thread_id, request_id), - base::Bind(&BluetoothDispatcherHost::OnCharacteristicReadValueError, - weak_ptr_on_ui_thread_, thread_id, request_id)); + std::vector characteristics_instance_ids; + std::vector characteristics_uuids; + std::vector characteristics_properties; + + for (BluetoothGattCharacteristic* characteristic : + query_result.service->GetCharacteristics()) { + if (!BluetoothBlacklist::Get().IsExcluded(characteristic->GetUUID()) && + (characteristics_uuid.empty() || + characteristics_uuid == characteristic->GetUUID().canonical_value())) { + const std::string& characteristic_instance_id = + characteristic->GetIdentifier(); + + characteristics_instance_ids.push_back(characteristic_instance_id); + characteristics_uuids.push_back( + characteristic->GetUUID().canonical_value()); + characteristics_properties.push_back( + static_cast(characteristic->GetProperties())); + + auto insert_result = characteristic_to_service_.insert( + make_pair(characteristic_instance_id, service_instance_id)); + + // If value is already in map, DCHECK it's valid. + if (!insert_result.second) + DCHECK(insert_result.first->second == service_instance_id); + } + } + + if (!characteristics_instance_ids.empty()) { + RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome::SUCCESS); + Send(new BluetoothMsg_GetCharacteristicsSuccess( + thread_id, request_id, characteristics_instance_ids, + characteristics_uuids, characteristics_properties)); + return; + } + RecordGetCharacteristicsOutcome( + characteristics_uuid.empty() + ? UMAGetCharacteristicOutcome::NO_CHARACTERISTICS + : UMAGetCharacteristicOutcome::NOT_FOUND); + Send(new BluetoothMsg_GetCharacteristicsError( + thread_id, request_id, + characteristics_uuid.empty() + ? WebBluetoothError::NO_CHARACTERISTICS_FOUND + : WebBluetoothError::CHARACTERISTIC_NOT_FOUND)); } -void BluetoothDispatcherHost::OnWriteValue( +void BluetoothDispatcherHost::OnReadValue( int thread_id, int request_id, int frame_routing_id, - const std::string& characteristic_instance_id, - const std::vector& value) { + const std::string& characteristic_instance_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RecordWebBluetoothFunctionCall( - UMAWebBluetoothFunction::CHARACTERISTIC_WRITE_VALUE); - - // Length check per step 3 of writeValue algorithm: - // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue - // We perform the length check on the renderer side. So if we - // get a value with length > 512, we can assume it's a hostile - // renderer and kill it. - if (value.size() > 512) { - bad_message::ReceivedBadMessage( - this, bad_message::BDH_INVALID_WRITE_VALUE_LENGTH); - return; - } + UMAWebBluetoothFunction::CHARACTERISTIC_READ_VALUE); const CacheQueryResult query_result = QueryCacheForCharacteristic( GetOrigin(frame_routing_id), characteristic_instance_id); @@ -914,16 +1011,24 @@ void BluetoothDispatcherHost::OnWriteValue( } if (query_result.outcome != CacheQueryOutcome::SUCCESS) { - RecordCharacteristicWriteValueOutcome(query_result.outcome); - Send(new BluetoothMsg_WriteCharacteristicValueError( + RecordCharacteristicReadValueOutcome(query_result.outcome); + Send(new BluetoothMsg_ReadCharacteristicValueError( thread_id, request_id, query_result.GetWebError())); return; } - query_result.characteristic->WriteRemoteCharacteristic( - value, base::Bind(&BluetoothDispatcherHost::OnWriteValueSuccess, - weak_ptr_on_ui_thread_, thread_id, request_id), - base::Bind(&BluetoothDispatcherHost::OnWriteValueFailed, + if (BluetoothBlacklist::Get().IsExcludedFromReads( + query_result.characteristic->GetUUID())) { + RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::BLACKLISTED); + Send(new BluetoothMsg_ReadCharacteristicValueError( + thread_id, request_id, WebBluetoothError::BLACKLISTED_READ)); + return; + } + + query_result.characteristic->ReadRemoteCharacteristic( + base::Bind(&BluetoothDispatcherHost::OnCharacteristicValueRead, + weak_ptr_on_ui_thread_, thread_id, request_id), + base::Bind(&BluetoothDispatcherHost::OnCharacteristicReadValueError, weak_ptr_on_ui_thread_, thread_id, request_id)); } @@ -1028,6 +1133,181 @@ void BluetoothDispatcherHost::OnUnregisterCharacteristicObject( } } +void BluetoothDispatcherHost::OnGetAdapter( + base::Closure continuation, + scoped_refptr adapter) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + set_adapter(adapter); + continuation.Run(); +} + +void BluetoothDispatcherHost::OnRequestDeviceImpl( + int thread_id, + int request_id, + int frame_routing_id, + const std::vector& filters, + const std::vector& optional_services) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + VLOG(1) << "requestDevice called with the following filters: "; + for (const BluetoothScanFilter& filter : filters) { + VLOG(1) << "Name: " << filter.name; + VLOG(1) << "Name Prefix: " << filter.namePrefix; + VLOG(1) << "Services:"; + VLOG(1) << "\t["; + for (const BluetoothUUID& service : filter.services) + VLOG(1) << "\t\t" << service.value(); + VLOG(1) << "\t]"; + } + + VLOG(1) << "requestDevice called with the following optional services: "; + for (const BluetoothUUID& service : optional_services) + VLOG(1) << "\t" << service.value(); + + // Check blacklist to reject invalid filters and adjust optional_services. + if (BluetoothBlacklist::Get().IsExcluded(filters)) { + RecordRequestDeviceOutcome( + UMARequestDeviceOutcome::BLACKLISTED_SERVICE_IN_FILTER); + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, + WebBluetoothError::REQUEST_DEVICE_WITH_BLACKLISTED_UUID)); + return; + } + std::vector optional_services_blacklist_filtered( + optional_services); + BluetoothBlacklist::Get().RemoveExcludedUuids( + &optional_services_blacklist_filtered); + + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id); + WebContents* web_contents = + WebContents::FromRenderFrameHost(render_frame_host); + + if (!render_frame_host || !web_contents) { + DLOG(WARNING) << "Got a requestDevice IPC without a matching " + << "RenderFrameHost or WebContents: " << render_process_id_ + << ", " << frame_routing_id; + RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME); + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, + WebBluetoothError::REQUEST_DEVICE_WITHOUT_FRAME)); + return; + } + + const url::Origin requesting_origin = + render_frame_host->GetLastCommittedOrigin(); + const url::Origin embedding_origin = + web_contents->GetMainFrame()->GetLastCommittedOrigin(); + + // TODO(crbug.com/518042): Enforce correctly-delegated permissions instead of + // matching origins. When relaxing this, take care to handle non-sandboxed + // unique origins. + if (!embedding_origin.IsSameOriginWith(requesting_origin)) { + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, + WebBluetoothError::REQUEST_DEVICE_FROM_CROSS_ORIGIN_IFRAME)); + return; + } + // The above also excludes unique origins, which are not even same-origin with + // themselves. + DCHECK(!requesting_origin.unique()); + + DCHECK(adapter_.get()); + + if (!adapter_->IsPresent()) { + VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice."; + RecordRequestDeviceOutcome( + UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT); + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER)); + return; + } + + // The renderer should never send empty filters. + if (HasEmptyOrInvalidFilter(filters)) { + bad_message::ReceivedBadMessage(this, + bad_message::BDH_EMPTY_OR_INVALID_FILTERS); + return; + } + + switch (GetContentClient()->browser()->AllowWebBluetooth( + web_contents->GetBrowserContext(), requesting_origin, embedding_origin)) { + case ContentBrowserClient::AllowWebBluetoothResult::BLOCK_POLICY: { + RecordRequestDeviceOutcome( + UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_POLICY_DISABLED); + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, + WebBluetoothError::CHOOSER_NOT_SHOWN_API_LOCALLY_DISABLED)); + return; + } + case ContentBrowserClient::AllowWebBluetoothResult:: + BLOCK_GLOBALLY_DISABLED: { + // Log to the developer console. + web_contents->GetMainFrame()->AddMessageToConsole( + content::CONSOLE_MESSAGE_LEVEL_LOG, + "Bluetooth permission has been blocked."); + // Block requests. + RecordRequestDeviceOutcome( + UMARequestDeviceOutcome::BLUETOOTH_GLOBALLY_DISABLED); + Send(new BluetoothMsg_RequestDeviceError( + thread_id, request_id, + WebBluetoothError::CHOOSER_NOT_SHOWN_API_GLOBALLY_DISABLED)); + return; + } + case ContentBrowserClient::AllowWebBluetoothResult::ALLOW: + break; + } + + // Create storage for the information that backs the chooser, and show the + // chooser. + RequestDeviceSession* const session = new RequestDeviceSession( + thread_id, request_id, frame_routing_id, requesting_origin, filters, + optional_services_blacklist_filtered); + int chooser_id = request_device_sessions_.Add(session); + + BluetoothChooser::EventHandler chooser_event_handler = + base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent, + weak_ptr_on_ui_thread_, chooser_id); + if (WebContentsDelegate* delegate = web_contents->GetDelegate()) { + session->chooser = + delegate->RunBluetoothChooser(render_frame_host, chooser_event_handler); + } + if (!session->chooser) { + LOG(WARNING) + << "No Bluetooth chooser implementation; falling back to first device."; + session->chooser.reset( + new FirstDeviceBluetoothChooser(chooser_event_handler)); + } + + if (!session->chooser->CanAskForScanningPermission()) { + VLOG(1) << "Closing immediately because Chooser cannot obtain permission."; + OnBluetoothChooserEvent(chooser_id, + BluetoothChooser::Event::DENIED_PERMISSION, ""); + return; + } + + // Populate the initial list of devices. + VLOG(1) << "Populating " << adapter_->GetDevices().size() + << " devices in chooser " << chooser_id; + for (const device::BluetoothDevice* device : adapter_->GetDevices()) { + VLOG(1) << "\t" << device->GetAddress(); + session->AddFilteredDevice(*device); + } + + if (!session->chooser) { + // If the dialog's closing, no need to do any of the rest of this. + return; + } + + if (!adapter_->IsPowered()) { + session->chooser->SetAdapterPresence( + BluetoothChooser::AdapterPresence::POWERED_OFF); + return; + } + + StartDeviceDiscovery(session, chooser_id); +} + void BluetoothDispatcherHost::OnDiscoverySessionStarted( int chooser_id, scoped_ptr discovery_session) { @@ -1074,38 +1354,35 @@ void BluetoothDispatcherHost::OnBluetoothChooserEvent( switch (event) { case BluetoothChooser::Event::RESCAN: StartDeviceDiscovery(session, chooser_id); - break; + // No need to close the chooser so we return. + return; case BluetoothChooser::Event::DENIED_PERMISSION: case BluetoothChooser::Event::CANCELLED: - case BluetoothChooser::Event::SELECTED: { - // Synchronously ensure nothing else calls into the chooser after it has - // asked to be closed. - session->chooser.reset(); - - // Yield to the event loop to make sure we don't destroy the session - // within a BluetoothDispatcherHost stack frame. - if (!base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&BluetoothDispatcherHost::FinishClosingChooser, - weak_ptr_on_ui_thread_, chooser_id, event, - device_id))) { - LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog."; - } + case BluetoothChooser::Event::SELECTED: break; - } case BluetoothChooser::Event::SHOW_OVERVIEW_HELP: - ShowBluetoothOverviewLink(); - break; - case BluetoothChooser::Event::SHOW_PAIRING_HELP: - ShowBluetoothPairingLink(); + VLOG(1) << "Overview Help link pressed."; break; case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP: - ShowBluetoothAdapterOffLink(); + VLOG(1) << "Adapter Off Help link pressed."; break; case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP: - ShowNeedLocationLink(); + VLOG(1) << "Need Location Help link pressed."; break; } + + // Synchronously ensure nothing else calls into the chooser after it has + // asked to be closed. + session->chooser.reset(); + + // Yield to the event loop to make sure we don't destroy the session + // within a BluetoothDispatcherHost stack frame. + if (!base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&BluetoothDispatcherHost::FinishClosingChooser, + weak_ptr_on_ui_thread_, chooser_id, event, device_id))) { + LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog."; + } } void BluetoothDispatcherHost::FinishClosingChooser( @@ -1116,13 +1393,12 @@ void BluetoothDispatcherHost::FinishClosingChooser( RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id); DCHECK(session) << "Session removed unexpectedly."; - if (event == BluetoothChooser::Event::CANCELLED) { - RecordRequestDeviceOutcome( - UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED); - VLOG(1) << "Bluetooth chooser cancelled"; + if ((event != BluetoothChooser::Event::DENIED_PERMISSION) && + (event != BluetoothChooser::Event::SELECTED)) { + RecordRequestDeviceOutcome(OutcomeFromChooserEvent(event)); Send(new BluetoothMsg_RequestDeviceError( session->thread_id, session->request_id, - WebBluetoothError::ChooserCancelled)); + WebBluetoothError::CHOOSER_CANCELLED)); request_device_sessions_.Remove(chooser_id); return; } @@ -1132,7 +1408,7 @@ void BluetoothDispatcherHost::FinishClosingChooser( VLOG(1) << "Bluetooth chooser denied permission"; Send(new BluetoothMsg_RequestDeviceError( session->thread_id, session->request_id, - WebBluetoothError::ChooserDeniedPermission)); + WebBluetoothError::CHOOSER_NOT_SHOWN_USER_DENIED_PERMISSION_TO_SCAN)); request_device_sessions_.Remove(chooser_id); return; } @@ -1147,20 +1423,29 @@ void BluetoothDispatcherHost::FinishClosingChooser( RecordRequestDeviceOutcome(UMARequestDeviceOutcome::CHOSEN_DEVICE_VANISHED); Send(new BluetoothMsg_RequestDeviceError( session->thread_id, session->request_id, - WebBluetoothError::ChosenDeviceVanished)); + WebBluetoothError::CHOSEN_DEVICE_VANISHED)); request_device_sessions_.Remove(chooser_id); return; } - VLOG(1) << "Device: " << device->GetName(); - VLOG(1) << "UUIDs: "; - for (BluetoothUUID uuid : device->GetUUIDs()) - VLOG(1) << "\t" << uuid.canonical_value(); - const std::string& device_id_for_origin = allowed_devices_map_.AddDevice( session->origin, device->GetAddress(), session->filters, session->optional_services); + VLOG(1) << "Device: " << device->GetName(); + VLOG(1) << "UUIDs: "; + + device::BluetoothDevice::UUIDList filtered_uuids; + for (BluetoothUUID uuid : device->GetUUIDs()) { + if (allowed_devices_map_.IsOriginAllowedToAccessService( + session->origin, device_id_for_origin, uuid.canonical_value())) { + VLOG(1) << "\t Allowed: " << uuid.canonical_value(); + filtered_uuids.push_back(uuid); + } else { + VLOG(1) << "\t Not Allowed: " << uuid.canonical_value(); + } + } + content::BluetoothDevice device_ipc( device_id_for_origin, // id device->GetName(), // name @@ -1174,7 +1459,7 @@ void BluetoothDispatcherHost::FinishClosingChooser( device->GetProductID(), // product_id device->GetDeviceID(), // product_version content::BluetoothDevice::UUIDsFromBluetoothUUIDs( - device->GetUUIDs())); // uuids + filtered_uuids)); // uuids RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS); Send(new BluetoothMsg_RequestDeviceSuccess(session->thread_id, session->request_id, device_ipc)); @@ -1184,14 +1469,16 @@ void BluetoothDispatcherHost::FinishClosingChooser( void BluetoothDispatcherHost::OnGATTConnectionCreated( int thread_id, int request_id, + int frame_routing_id, const std::string& device_id, base::TimeTicks start_time, scoped_ptr connection) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - connections_.push_back(std::move(connection)); RecordConnectGATTTimeSuccess(base::TimeTicks::Now() - start_time); RecordConnectGATTOutcome(UMAConnectGATTOutcome::SUCCESS); - Send(new BluetoothMsg_ConnectGATTSuccess(thread_id, request_id, device_id)); + connected_devices_map_->InsertOrReplace(frame_routing_id, device_id, + std::move(connection)); + Send(new BluetoothMsg_GATTServerConnectSuccess(thread_id, request_id)); } void BluetoothDispatcherHost::OnCreateGATTConnectionError( @@ -1206,8 +1493,8 @@ void BluetoothDispatcherHost::OnCreateGATTConnectionError( // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt RecordConnectGATTTimeFailed(base::TimeTicks::Now() - start_time); // RecordConnectGATTOutcome is called by TranslateConnectError. - Send(new BluetoothMsg_ConnectGATTError(thread_id, request_id, - TranslateConnectError(error_code))); + Send(new BluetoothMsg_GATTServerConnectError( + thread_id, request_id, TranslateConnectError(error_code))); } void BluetoothDispatcherHost::AddToServicesMapAndSendGetPrimaryServiceSuccess( @@ -1249,24 +1536,6 @@ void BluetoothDispatcherHost::OnCharacteristicReadValueError( TranslateGATTError(error_code, UMAGATTOperation::CHARACTERISTIC_READ))); } -void BluetoothDispatcherHost::OnWriteValueSuccess(int thread_id, - int request_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::SUCCESS); - Send(new BluetoothMsg_WriteCharacteristicValueSuccess(thread_id, request_id)); -} - -void BluetoothDispatcherHost::OnWriteValueFailed( - int thread_id, - int request_id, - device::BluetoothGattService::GattErrorCode error_code) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // TranslateGATTError calls RecordGATTOperationOutcome. - Send(new BluetoothMsg_WriteCharacteristicValueError( - thread_id, request_id, - TranslateGATTError(error_code, UMAGATTOperation::CHARACTERISTIC_WRITE))); -} - void BluetoothDispatcherHost::OnStartNotifySessionSuccess( int thread_id, int request_id, @@ -1355,6 +1624,12 @@ BluetoothDispatcherHost::QueryCacheForService( result.service = result.device->GetGattService(service_instance_id); if (result.service == nullptr) { result.outcome = CacheQueryOutcome::NO_SERVICE; + } else if (!allowed_devices_map_.IsOriginAllowedToAccessService( + origin, device_id, + result.service->GetUUID().canonical_value())) { + bad_message::ReceivedBadMessage( + this, bad_message::BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN); + return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER); } return result; } @@ -1408,24 +1683,4 @@ bool BluetoothDispatcherHost::CanFrameAccessCharacteristicInstance( .outcome != CacheQueryOutcome::BAD_RENDERER; } -void BluetoothDispatcherHost::ShowBluetoothOverviewLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowBluetoothPairingLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowBluetoothAdapterOffLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowNeedLocationLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - } // namespace content diff --git a/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.h b/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.h index 7bae29ca931..e3a4d6178bd 100644 --- a/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.h +++ b/chromium/content/browser/bluetooth/bluetooth_dispatcher_host.h @@ -14,6 +14,8 @@ #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "content/browser/bluetooth/bluetooth_allowed_devices_map.h" +#include "content/browser/bluetooth/bluetooth_metrics.h" +#include "content/common/bluetooth/bluetooth_messages.h" #include "content/public/browser/bluetooth_chooser.h" #include "content/public/browser/browser_message_filter.h" #include "device/bluetooth/bluetooth_adapter.h" @@ -53,6 +55,29 @@ class CONTENT_EXPORT BluetoothDispatcherHost final void SetBluetoothAdapterForTesting( scoped_refptr mock_adapter); + // TODO(ortuno): We temporarily make this a public struct so that + // both BluetoothDispatcherHost and WebBluetoothServiceImpl can use it, + // while we move functions from BluetoothDispatcherHost to + // WebBluetoothServiceImpl. + // https://crbug.com/508771 + struct CacheQueryResult { + CacheQueryResult(); + CacheQueryResult(CacheQueryOutcome outcome); + ~CacheQueryResult(); + blink::WebBluetoothError GetWebError() const; + device::BluetoothDevice* device = nullptr; + device::BluetoothGattService* service = nullptr; + device::BluetoothGattCharacteristic* characteristic = nullptr; + CacheQueryOutcome outcome = CacheQueryOutcome::SUCCESS; + }; + + // Queries the platform cache for a characteristic with + // |characteristic_instance_id|. Fills in the |outcome| field, and |device|, + // |service| and |characteristic| fields if successful. + CacheQueryResult QueryCacheForCharacteristic( + const url::Origin& origin, + const std::string& characteristic_instance_id); + protected: ~BluetoothDispatcherHost() override; @@ -60,10 +85,32 @@ class CONTENT_EXPORT BluetoothDispatcherHost final friend class base::DeleteHelper; friend struct BrowserThread::DeleteOnThread; - struct CacheQueryResult; struct RequestDeviceSession; struct PrimaryServicesRequest; + // Map to keep track of connections. Inserting and removing connections + // will update the Web Contents for the frame. Upon destruction + // the map will clear Web Contents of Bluetooth connections. + struct ConnectedDevicesMap { + ConnectedDevicesMap(int render_process_id); + ~ConnectedDevicesMap(); + bool HasActiveConnection(const std::string& device_id); + void InsertOrReplace( + int frame_routing_id, + const std::string& device_id, + scoped_ptr connection); + void Remove(int frame_routing_id, const std::string& device_id); + void IncrementBluetoothConnectedDeviceCount(int frame_routing_id); + void DecrementBluetoothConnectedDeviceCount(int frame_routing_id); + + int render_process_id_; + std::unordered_map> + device_id_to_connection_map_; + // Keeps track of which frame is connected to which device so that + // we can clean up the WebContents in our destructor. + std::set> frame_ids_device_ids_; + }; + // Set |adapter_| to a BluetoothAdapter instance and register observers, // releasing references to previous |adapter_|. void set_adapter(scoped_refptr adapter); @@ -103,10 +150,13 @@ class CONTENT_EXPORT BluetoothDispatcherHost final int frame_routing_id, const std::vector& filters, const std::vector& optional_services); - void OnConnectGATT(int thread_id, - int request_id, - int frame_routing_id, - const std::string& device_id); + void OnGATTServerConnect(int thread_id, + int request_id, + int frame_routing_id, + const std::string& device_id); + void OnGATTServerDisconnect(int thread_id, + int frame_routing_id, + const std::string& device_id); void OnGetPrimaryService(int thread_id, int request_id, int frame_routing_id, @@ -117,15 +167,15 @@ class CONTENT_EXPORT BluetoothDispatcherHost final int frame_routing_id, const std::string& service_instance_id, const std::string& characteristic_uuid); + void OnGetCharacteristics(int thread_id, + int request_id, + int frame_routing_id, + const std::string& service_instance_id, + const std::string& characteristics_uuid); void OnReadValue(int thread_id, int request_id, int frame_routing_id, const std::string& characteristic_instance_id); - void OnWriteValue(int thread_id, - int request_id, - int frame_routing_id, - const std::string& characteristic_instance_id, - const std::vector& value); void OnStartNotifications(int thread_id, int request_id, int frame_routing_id, @@ -143,6 +193,17 @@ class CONTENT_EXPORT BluetoothDispatcherHost final int frame_routing_id, const std::string& characteristic_instance_id); + // Callbacks for BluetoothDevice::OnRequestDevice. + // If necessary, the adapter must be obtained before continuing to Impl. + void OnGetAdapter(base::Closure continuation, + scoped_refptr adapter); + void OnRequestDeviceImpl( + int thread_id, + int request_id, + int frame_routing_id, + const std::vector& filters, + const std::vector& optional_services); + // Callbacks for BluetoothAdapter::StartDiscoverySession. void OnDiscoverySessionStarted( int chooser_id, @@ -165,6 +226,7 @@ class CONTENT_EXPORT BluetoothDispatcherHost final void OnGATTConnectionCreated( int thread_id, int request_id, + int frame_routing_id, const std::string& device_id, base::TimeTicks start_time, scoped_ptr connection); @@ -191,12 +253,6 @@ class CONTENT_EXPORT BluetoothDispatcherHost final int request_id, device::BluetoothGattService::GattErrorCode); - // Callbacks for BluetoothGattCharacteristic::WriteRemoteCharacteristic. - void OnWriteValueSuccess(int thread_id, int request_id); - void OnWriteValueFailed(int thread_id, - int request_id, - device::BluetoothGattService::GattErrorCode); - // Callbacks for BluetoothGattCharacteristic::StartNotifySession. void OnStartNotifySessionSuccess( int thread_id, @@ -228,12 +284,6 @@ class CONTENT_EXPORT BluetoothDispatcherHost final // in the |outcome| field, and |device| and |service| fields if successful. CacheQueryResult QueryCacheForService(const url::Origin& origin, const std::string& service_instance_id); - // Queries the platform cache for a characteristic with - // |characteristic_instance_id|. Fills in the |outcome| field, and |device|, - // |service| and |characteristic| fields if successful. - CacheQueryResult QueryCacheForCharacteristic( - const url::Origin& origin, - const std::string& characteristic_instance_id); // Adds the PrimaryServicesRequest to the vector of pending services requests // for that device. @@ -251,12 +301,6 @@ class CONTENT_EXPORT BluetoothDispatcherHost final int frame_routing_id, const std::string& characteristic_instance_id); - // Show help pages from the chooser dialog. - void ShowBluetoothOverviewLink(); - void ShowBluetoothPairingLink(); - void ShowBluetoothAdapterOffLink(); - void ShowNeedLocationLink(); - int render_process_id_; // Maps a (thread_id,request_id) to information about its requestDevice call, @@ -297,9 +341,8 @@ class CONTENT_EXPORT BluetoothDispatcherHost final // sessions when other sessions are active. base::Timer discovery_session_timer_; - // Retain BluetoothGattConnection objects to keep connections open. - // TODO(scheib): Destroy as connections are closed. http://crbug.com/539643 - ScopedVector connections_; + // Retains BluetoothGattConnection objects to keep connections open. + scoped_ptr connected_devices_map_; // Map of device_address's to primary-services requests that need responses // when that device's service discovery completes. diff --git a/chromium/content/browser/bluetooth/bluetooth_metrics.cc b/chromium/content/browser/bluetooth/bluetooth_metrics.cc index ef829203bba..32f7c73dfa7 100644 --- a/chromium/content/browser/bluetooth/bluetooth_metrics.cc +++ b/chromium/content/browser/bluetooth/bluetooth_metrics.cc @@ -97,7 +97,7 @@ void RecordRequestDeviceArguments( RecordUnionOfServices(filters, optional_services); } -// connectGATT +// GATTServer.Connect void RecordConnectGATTOutcome(UMAConnectGATTOutcome outcome) { UMA_HISTOGRAM_ENUMERATION("Bluetooth.Web.ConnectGATT.Outcome", @@ -150,7 +150,8 @@ void RecordGetCharacteristicOutcome(CacheQueryOutcome outcome) { switch (outcome) { case CacheQueryOutcome::SUCCESS: case CacheQueryOutcome::BAD_RENDERER: - NOTREACHED() << "No need to record a success or renderer crash"; + // No need to record a success or renderer crash. + NOTREACHED(); return; case CacheQueryOutcome::NO_DEVICE: RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_DEVICE); @@ -169,6 +170,38 @@ void RecordGetCharacteristicCharacteristic(const std::string& characteristic) { HashUUID(characteristic)); } +// getCharacteristics + +void RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome outcome) { + UMA_HISTOGRAM_ENUMERATION( + "Bluetooth.Web.GetCharacteristics.Outcome", static_cast(outcome), + static_cast(UMAGetCharacteristicOutcome::COUNT)); +} + +void RecordGetCharacteristicsOutcome(CacheQueryOutcome outcome) { + switch (outcome) { + case CacheQueryOutcome::SUCCESS: + case CacheQueryOutcome::BAD_RENDERER: + // No need to record a success or renderer crash. + NOTREACHED(); + return; + case CacheQueryOutcome::NO_DEVICE: + RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome::NO_DEVICE); + return; + case CacheQueryOutcome::NO_SERVICE: + RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome::NO_SERVICE); + return; + case CacheQueryOutcome::NO_CHARACTERISTIC: + NOTREACHED(); + return; + } +} + +void RecordGetCharacteristicsCharacteristic(const std::string& characteristic) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Bluetooth.Web.GetCharacteristics.Characteristic", + HashUUID(characteristic)); +} + // GATT Operations void RecordGATTOperationOutcome(UMAGATTOperation operation, @@ -195,7 +228,8 @@ static UMAGATTOperationOutcome TranslateCacheQueryOutcomeToGATTOperationOutcome( switch (outcome) { case CacheQueryOutcome::SUCCESS: case CacheQueryOutcome::BAD_RENDERER: - NOTREACHED() << "No need to record success or renderer crash"; + // No need to record a success or renderer crash. + NOTREACHED(); return UMAGATTOperationOutcome::NOT_SUPPORTED; case CacheQueryOutcome::NO_DEVICE: return UMAGATTOperationOutcome::NO_DEVICE; diff --git a/chromium/content/browser/bluetooth/bluetooth_metrics.h b/chromium/content/browser/bluetooth/bluetooth_metrics.h index fa508590177..872008b8513 100644 --- a/chromium/content/browser/bluetooth/bluetooth_metrics.h +++ b/chromium/content/browser/bluetooth/bluetooth_metrics.h @@ -31,6 +31,8 @@ enum class UMAWebBluetoothFunction { CHARACTERISTIC_WRITE_VALUE = 5, CHARACTERISTIC_START_NOTIFICATIONS = 6, CHARACTERISTIC_STOP_NOTIFICATIONS = 7, + REMOTE_GATT_SERVER_DISCONNECT = 8, + SERVICE_GET_CHARACTERISTICS = 9, // NOTE: Add new actions immediately above this line. Make sure to update // the enum list in tools/metrics/histograms/histograms.xml accordingly. COUNT @@ -62,6 +64,12 @@ enum class UMARequestDeviceOutcome { CHOSEN_DEVICE_VANISHED = 8, BLUETOOTH_CHOOSER_CANCELLED = 9, BLUETOOTH_CHOOSER_DENIED_PERMISSION = 10, + BLACKLISTED_SERVICE_IN_FILTER = 11, + BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED = 12, + ADAPTER_OFF_HELP_LINK_PRESSED = 13, + NEED_LOCATION_HELP_LINK_PRESSED = 14, + BLUETOOTH_CHOOSER_POLICY_DISABLED = 15, + BLUETOOTH_GLOBALLY_DISABLED = 16, // NOTE: Add new requestDevice() outcomes immediately above this line. Make // sure to update the enum list in // tools/metrics/histograms/histograms.xml accordingly. @@ -145,6 +153,8 @@ enum class UMAGetCharacteristicOutcome { NO_DEVICE = 1, NO_SERVICE = 2, NOT_FOUND = 3, + BLACKLISTED = 4, + NO_CHARACTERISTICS = 5, // Note: Add new outcomes immediately above this line. // Make sure to update the enum list in // tools/metrisc/histogram/histograms.xml accordingly. @@ -160,6 +170,17 @@ void RecordGetCharacteristicOutcome(CacheQueryOutcome outcome); // Records the UUID of the characteristic used when calling getCharacteristic. void RecordGetCharacteristicCharacteristic(const std::string& characteristic); +// getCharacteristics() Metrics +// There should be a call to this function for every call to +// Send(BluetoothMsg_GetCharacteristicsSuccess) and +// Send(BluetoothMsg_GetCharacteristicsError). +void RecordGetCharacteristicsOutcome(UMAGetCharacteristicOutcome outcome); +// Records the outcome of the cache query for getCharacteristics. Should only be +// called if QueryCacheForService fails. +void RecordGetCharacteristicsOutcome(CacheQueryOutcome outcome); +// Records the UUID of the characteristic used when calling getCharacteristic. +void RecordGetCharacteristicsCharacteristic(const std::string& characteristic); + // GATT Operations Metrics // These are the possible outcomes when performing GATT operations i.e. @@ -178,6 +199,7 @@ enum UMAGATTOperationOutcome { NOT_AUTHORIZED = 10, NOT_PAIRED = 11, NOT_SUPPORTED = 12, + BLACKLISTED = 13, // Note: Add new GATT Outcomes immediately above this line. // Make sure to update the enum list in // tools/metrics/histograms/histograms.xml accordingly. diff --git a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc new file mode 100644 index 00000000000..ea627d222e0 --- /dev/null +++ b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc @@ -0,0 +1,156 @@ +// 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 "content/browser/bluetooth/web_bluetooth_service_impl.h" + +#include + +#include "content/browser/bluetooth/bluetooth_blacklist.h" +#include "content/browser/bluetooth/bluetooth_dispatcher_host.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/public/browser/render_frame_host.h" +#include "device/bluetooth/bluetooth_gatt_characteristic.h" + +using device::BluetoothGattService; + +namespace content { + +namespace { + +blink::mojom::WebBluetoothError TranslateGATTErrorAndRecord( + BluetoothGattService::GattErrorCode error_code, + UMAGATTOperation operation) { + switch (error_code) { + case BluetoothGattService::GATT_ERROR_UNKNOWN: + RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::UNKNOWN); + return blink::mojom::WebBluetoothError::GATT_UNKNOWN_ERROR; + case BluetoothGattService::GATT_ERROR_FAILED: + RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::FAILED); + return blink::mojom::WebBluetoothError::GATT_UNKNOWN_FAILURE; + case BluetoothGattService::GATT_ERROR_IN_PROGRESS: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::IN_PROGRESS); + return blink::mojom::WebBluetoothError::GATT_OPERATION_IN_PROGRESS; + case BluetoothGattService::GATT_ERROR_INVALID_LENGTH: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::INVALID_LENGTH); + return blink::mojom::WebBluetoothError::GATT_INVALID_ATTRIBUTE_LENGTH; + case BluetoothGattService::GATT_ERROR_NOT_PERMITTED: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::NOT_PERMITTED); + return blink::mojom::WebBluetoothError::GATT_NOT_PERMITTED; + case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::NOT_AUTHORIZED); + return blink::mojom::WebBluetoothError::GATT_NOT_AUTHORIZED; + case BluetoothGattService::GATT_ERROR_NOT_PAIRED: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::NOT_PAIRED); + return blink::mojom::WebBluetoothError::GATT_NOT_PAIRED; + case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED: + RecordGATTOperationOutcome(operation, + UMAGATTOperationOutcome::NOT_SUPPORTED); + return blink::mojom::WebBluetoothError::GATT_NOT_SUPPORTED; + } + NOTREACHED(); + return blink::mojom::WebBluetoothError::GATT_UNTRANSLATED_ERROR_CODE; +} + +} // namespace + +using CacheQueryResult = BluetoothDispatcherHost::CacheQueryResult; + +WebBluetoothServiceImpl::WebBluetoothServiceImpl( + RenderFrameHost* render_frame_host, + blink::mojom::WebBluetoothServiceRequest request) + : render_frame_host_(render_frame_host), + binding_(this, std::move(request)), + weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +WebBluetoothServiceImpl::~WebBluetoothServiceImpl() {} + +void WebBluetoothServiceImpl::RemoteCharacteristicWriteValue( + const mojo::String& characteristic_instance_id, + mojo::Array value, + const RemoteCharacteristicWriteValueCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + RecordWebBluetoothFunctionCall( + UMAWebBluetoothFunction::CHARACTERISTIC_WRITE_VALUE); + + // We perform the length check on the renderer side. So if we + // get a value with length > 512, we can assume it's a hostile + // renderer and kill it. + if (value.size() > 512) { + CrashRendererAndClosePipe(bad_message::BDH_INVALID_WRITE_VALUE_LENGTH); + return; + } + + const CacheQueryResult query_result = + GetBluetoothDispatcherHost()->QueryCacheForCharacteristic( + GetOrigin(), characteristic_instance_id); + + if (query_result.outcome == CacheQueryOutcome::BAD_RENDERER) { + binding_.Close(); + return; + } + + if (query_result.outcome != CacheQueryOutcome::SUCCESS) { + RecordCharacteristicWriteValueOutcome(query_result.outcome); + callback.Run(query_result.GetWebError()); + return; + } + + if (BluetoothBlacklist::Get().IsExcludedFromWrites( + query_result.characteristic->GetUUID())) { + RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::BLACKLISTED); + callback.Run(blink::mojom::WebBluetoothError::BLACKLISTED_WRITE); + return; + } + + query_result.characteristic->WriteRemoteCharacteristic( + value.To>(), + base::Bind(&WebBluetoothServiceImpl::OnWriteValueSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&WebBluetoothServiceImpl::OnWriteValueFailed, + weak_ptr_factory_.GetWeakPtr(), callback)); +} + +void WebBluetoothServiceImpl::OnWriteValueSuccess( + const RemoteCharacteristicWriteValueCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::SUCCESS); + callback.Run(blink::mojom::WebBluetoothError::SUCCESS); +} + +void WebBluetoothServiceImpl::OnWriteValueFailed( + const RemoteCharacteristicWriteValueCallback& callback, + device::BluetoothGattService::GattErrorCode error_code) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + callback.Run(TranslateGATTErrorAndRecord( + error_code, UMAGATTOperation::CHARACTERISTIC_WRITE)); +} + +RenderProcessHost* WebBluetoothServiceImpl::GetRenderProcessHost() { + return render_frame_host_->GetProcess(); +} + +BluetoothDispatcherHost* WebBluetoothServiceImpl::GetBluetoothDispatcherHost() { + RenderProcessHostImpl* render_process_host_impl = + static_cast(GetRenderProcessHost()); + return render_process_host_impl->GetBluetoothDispatcherHost(); +} + +void WebBluetoothServiceImpl::CrashRendererAndClosePipe( + bad_message::BadMessageReason reason) { + bad_message::ReceivedBadMessage(GetRenderProcessHost(), reason); + binding_.Close(); +} + +url::Origin WebBluetoothServiceImpl::GetOrigin() { + return render_frame_host_->GetLastCommittedOrigin(); +} + +} // namespace content diff --git a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h new file mode 100644 index 00000000000..45f9e8c65bd --- /dev/null +++ b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h @@ -0,0 +1,77 @@ +// 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 CONTENT_BROWSER_BLUETOOTH_WEB_BLUETOOTH_SERVICE_IMPL_H_ +#define CONTENT_BROWSER_BLUETOOTH_WEB_BLUETOOTH_SERVICE_IMPL_H_ + +#include "base/macros.h" +#include "content/browser/bad_message.h" +#include "content/common/content_export.h" +#include "device/bluetooth/bluetooth_gatt_service.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom.h" + +namespace url { +class Origin; +} // namespace url + +namespace content { + +class BluetoothDispatcherHost; +class RenderFrameHost; +class RenderProcessHost; + +// Implementation of Mojo WebBluetoothService located in +// third_party/WebKit/public/platform/modules/bluetooth. +// It handles Web Bluetooth API requests coming from Blink / renderer +// process and uses the platform abstraction of device/bluetooth. +// WebBluetoothServiceImpl is not thread-safe and should be created on the +// UI thread as required by device/bluetooth. +// This class is instantiated on-demand via Mojo's ConnectToRemoteService +// from the renderer when the first Web Bluetooth API request is handled. +// RenderFrameHostImpl will create an instance of this class and keep +// ownership of it. +class WebBluetoothServiceImpl : public blink::mojom::WebBluetoothService { + public: + // |render_frame_host|: The RFH that owns this instance. + // |request|: The instance will be bound to this request's pipe. + WebBluetoothServiceImpl(RenderFrameHost* render_frame_host, + blink::mojom::WebBluetoothServiceRequest request); + ~WebBluetoothServiceImpl() override; + + private: + // WebBluetoothService methods: + void RemoteCharacteristicWriteValue( + const mojo::String& characteristic_instance_id, + mojo::Array value, + const RemoteCharacteristicWriteValueCallback& callback) override; + + // Callbacks for BluetoothGattCharacteristic::WriteRemoteCharacteristic. + void OnWriteValueSuccess( + const RemoteCharacteristicWriteValueCallback& callback); + void OnWriteValueFailed( + const RemoteCharacteristicWriteValueCallback& callback, + device::BluetoothGattService::GattErrorCode error_code); + + RenderProcessHost* GetRenderProcessHost(); + BluetoothDispatcherHost* GetBluetoothDispatcherHost(); + void CrashRendererAndClosePipe(bad_message::BadMessageReason reason); + url::Origin GetOrigin(); + + // The RFH that owns this instance. + RenderFrameHost* render_frame_host_; + + // The lifetime of this instance is exclusively managed by the RFH that + // owns it so we use a "Binding" as opposed to a "StrongBinding" which deletes + // the service on pipe connection errors. + mojo::Binding binding_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(WebBluetoothServiceImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BLUETOOTH_WEB_BLUETOOTH_SERVICE_IMPL_H_ diff --git a/chromium/content/browser/browser_child_process_host_impl.cc b/chromium/content/browser/browser_child_process_host_impl.cc index 9461b624195..64b81eeb361 100644 --- a/chromium/content/browser/browser_child_process_host_impl.cc +++ b/chromium/content/browser/browser_child_process_host_impl.cc @@ -8,10 +8,12 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/debug/dump_without_crashing.h" +#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -35,18 +37,12 @@ #include "content/public/common/result_codes.h" #include "ipc/attachment_broker.h" #include "ipc/attachment_broker_privileged.h" -#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/embedder.h" #if defined(OS_MACOSX) #include "content/browser/mach_broker_mac.h" #endif - -#if defined(MOJO_SHELL_CLIENT) -#include "content/browser/mojo/mojo_shell_client_host.h" -#include "content/common/mojo/mojo_shell_connection_impl.h" -#endif - namespace content { namespace { @@ -132,7 +128,8 @@ BrowserChildProcessHostImpl::BrowserChildProcessHostImpl( : data_(process_type), delegate_(delegate), power_monitor_message_broadcaster_(this), - is_channel_connected_(false) { + is_channel_connected_(false), + notify_child_disconnected_(false) { data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId(); #if USE_ATTACHMENT_BROKER @@ -140,12 +137,12 @@ BrowserChildProcessHostImpl::BrowserChildProcessHostImpl( // child process. This ensures that when a test is being run in one of the // single process modes, the global attachment broker is the privileged // attachment broker, rather than an unprivileged attachment broker. -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) IPC::AttachmentBrokerPrivileged::CreateBrokerIfNeeded( MachBroker::GetInstance()); #else IPC::AttachmentBrokerPrivileged::CreateBrokerIfNeeded(); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) +#endif // defined(OS_MACOSX) #endif // USE_ATTACHMENT_BROKER child_process_host_.reset(ChildProcessHost::Create(this)); @@ -162,6 +159,11 @@ BrowserChildProcessHostImpl::BrowserChildProcessHostImpl( BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() { g_child_process_list.Get().remove(this); + + if (notify_child_disconnected_) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&NotifyProcessHostDisconnected, data_)); + } } // static @@ -176,6 +178,28 @@ void BrowserChildProcessHostImpl::TerminateAll() { } } +// static +void BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags( + base::CommandLine* cmd_line) { + std::string enabled_features; + std::string disabled_features; + base::FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features, + &disabled_features); + if (!enabled_features.empty()) + cmd_line->AppendSwitchASCII(switches::kEnableFeatures, enabled_features); + if (!disabled_features.empty()) + cmd_line->AppendSwitchASCII(switches::kDisableFeatures, disabled_features); + + // If we run base::FieldTrials, we want to pass to their state to the + // child process so that it can act in accordance with each state. + std::string field_trial_states; + base::FieldTrialList::AllStatesToString(&field_trial_states); + if (!field_trial_states.empty()) { + cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, + field_trial_states); + } +} + void BrowserChildProcessHostImpl::Launch( SandboxedProcessLauncherDelegate* delegate, base::CommandLine* cmd_line, @@ -195,11 +219,11 @@ void BrowserChildProcessHostImpl::Launch( switches::kTraceToConsole, switches::kV, switches::kVModule, - "use-new-edk", // TODO(use_chrome_edk): temporary. }; cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches, arraysize(kForwardSwitches)); + notify_child_disconnected_ = true; child_process_.reset(new ChildProcessLauncher( delegate, cmd_line, @@ -256,13 +280,6 @@ void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) { child_process_host_->AddFilter(filter->GetFilter()); } -void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated( - const ChildProcessData& data) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), - BrowserChildProcessInstanceCreated(data)); -} - void BrowserChildProcessHostImpl::HistogramBadMessageTerminated( int process_type) { UMA_HISTOGRAM_ENUMERATION("ChildProcess.BadMessgeTerminated", process_type, @@ -287,6 +304,7 @@ void BrowserChildProcessHostImpl::OnChannelConnected(int32_t peer_pid) { DCHECK_CURRENTLY_ON(BrowserThread::IO); is_channel_connected_ = true; + notify_child_disconnected_ = true; #if defined(OS_WIN) // From this point onward, the exit of the child process is detected by an @@ -394,8 +412,6 @@ void BrowserChildProcessHostImpl::OnChildDisconnected() { } #endif } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&NotifyProcessHostDisconnected, data_)); delete delegate_; // Will delete us } @@ -405,6 +421,7 @@ bool BrowserChildProcessHostImpl::Send(IPC::Message* message) { void BrowserChildProcessHostImpl::OnProcessLaunchFailed() { delegate_->OnProcessLaunchFailed(); + notify_child_disconnected_ = false; delete delegate_; // Will delete us } @@ -414,25 +431,10 @@ void BrowserChildProcessHostImpl::OnProcessLaunched() { const base::Process& process = child_process_->GetProcess(); DCHECK(process.IsValid()); - if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { - mojo::embedder::ScopedPlatformHandle client_pipe; -#if defined(MOJO_SHELL_CLIENT) - if (IsRunningInMojoShell()) { - client_pipe = RegisterProcessWithBroker(process.Pid()); - } else -#endif - { - client_pipe = mojo::embedder::ChildProcessLaunched(process.Handle()); - } - Send(new ChildProcessMsg_SetMojoParentPipeHandle( - IPC::GetFileHandleForProcess( -#if defined(OS_WIN) - client_pipe.release().handle, -#else - client_pipe.release().fd, -#endif - process.Handle(), true))); - } + mojo::edk::ScopedPlatformHandle client_pipe = + mojo::edk::ChildProcessLaunched(process.Handle()); + Send(new ChildProcessMsg_SetMojoParentPipeHandle( + IPC::GetPlatformFileForTransit(client_pipe.release().handle, true))); #if defined(OS_WIN) // Start a WaitableEventWatcher that will invoke OnProcessExitedEarly if the diff --git a/chromium/content/browser/browser_child_process_host_impl.h b/chromium/content/browser/browser_child_process_host_impl.h index a5f3b6d528b..ce551a5da86 100644 --- a/chromium/content/browser/browser_child_process_host_impl.h +++ b/chromium/content/browser/browser_child_process_host_impl.h @@ -54,6 +54,11 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl // instance. static void TerminateAll(); + // Copies kEnableFeatures and kDisableFeatures to the command line. Generates + // them from the FeatureList override state, to take into account overrides + // from FieldTrials. + static void CopyFeatureAndFieldTrialFlags(base::CommandLine* cmd_line); + // BrowserChildProcessHost implementation: bool Send(IPC::Message* message) override; void Launch(SandboxedProcessLauncherDelegate* delegate, @@ -89,9 +94,6 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl // Adds an IPC message filter. void AddFilter(BrowserMessageFilter* filter); - // Called when an instance of a particular child is created in a page. - static void NotifyProcessInstanceCreated(const ChildProcessData& data); - static void HistogramBadMessageTerminated(int process_type); BrowserChildProcessHostDelegate* delegate() const { return delegate_; } @@ -135,6 +137,7 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl #endif bool is_channel_connected_; + bool notify_child_disconnected_; }; } // namespace content diff --git a/chromium/content/browser/browser_context.cc b/chromium/content/browser/browser_context.cc index fc3f644fba2..4b8396ed03e 100644 --- a/chromium/content/browser/browser_context.cc +++ b/chromium/content/browser/browser_context.cc @@ -6,11 +6,15 @@ #include #include +#include +#include #include +#include "base/guid.h" +#include "base/lazy_instance.h" +#include "base/rand_util.h" #include "build/build_config.h" - -#if !defined(OS_IOS) +#include "components/profile_service/user_id_map.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/indexed_db/indexed_db_context_impl.h" @@ -22,7 +26,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/site_instance.h" -#include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store.h" #include "net/ssl/channel_id_service.h" #include "net/ssl/channel_id_store.h" @@ -30,20 +33,24 @@ #include "net/url_request/url_request_context_getter.h" #include "storage/browser/database/database_tracker.h" #include "storage/browser/fileapi/external_mount_points.h" -#endif // !OS_IOS using base::UserDataAdapter; namespace content { -// Only ~BrowserContext() is needed on iOS. -#if !defined(OS_IOS) namespace { +base::LazyInstance> g_used_user_ids = + LAZY_INSTANCE_INITIALIZER; +base::LazyInstance>> +g_context_to_user_id = LAZY_INSTANCE_INITIALIZER; + // Key names on BrowserContext. const char kDownloadManagerKeyName[] = "download_manager"; const char kStoragePartitionMapKeyName[] = "content_storage_partition_map"; +const char kMojoWasInitialized[] = "mojo-was-initialized"; + #if defined(OS_CHROMEOS) const char kMountPointsKey[] = "mount_points"; #endif // defined(OS_CHROMEOS) @@ -78,8 +85,7 @@ void SaveSessionStateOnIOThread( const scoped_refptr& context_getter, AppCacheServiceImpl* appcache_service) { net::URLRequestContext* context = context_getter->GetURLRequestContext(); - context->cookie_store()->GetCookieMonster()-> - SetForceKeepSessionState(); + context->cookie_store()->SetForceKeepSessionState(); context->channel_id_service()->GetChannelIDStore()-> SetForceKeepSessionState(); appcache_service->set_force_keep_session_state(); @@ -255,11 +261,12 @@ void BrowserContext::DeliverPushMessage( BrowserContext* browser_context, const GURL& origin, int64_t service_worker_registration_id, - const std::string& data, + const PushEventPayload& payload, const base::Callback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - PushMessagingRouter::DeliverMessage( - browser_context, origin, service_worker_registration_id, data, callback); + PushMessagingRouter::DeliverMessage(browser_context, origin, + service_worker_registration_id, payload, + callback); } // static @@ -322,13 +329,44 @@ void BrowserContext::SetDownloadManagerForTesting( SetDownloadManager(browser_context, download_manager); } -#endif // !OS_IOS +void BrowserContext::Initialize( + BrowserContext* browser_context, + const base::FilePath& path) { + // Generate a GUID for |browser_context| to use as the Mojo user id. + std::string new_id = base::GenerateGUID(); + while (g_used_user_ids.Get().find(new_id) != g_used_user_ids.Get().end()) + new_id = base::GenerateGUID(); + + g_used_user_ids.Get().insert(new_id); + g_context_to_user_id.Get().push_back(std::make_pair(browser_context, new_id)); + + profile::AssociateMojoUserIDWithProfileDir(new_id, path); + browser_context->SetUserData(kMojoWasInitialized, + new base::SupportsUserData::Data); +} + +const std::string& BrowserContext::GetMojoUserIdFor( + BrowserContext* browser_context) { + CHECK(browser_context->GetUserData(kMojoWasInitialized)) + << "Attempting to get the mojo user id for a BrowserContext that was " + << "never Initialize()ed."; + + auto it = std::find_if( + g_context_to_user_id.Get().begin(), + g_context_to_user_id.Get().end(), + [&browser_context](const std::pair& p) { + return p.first == browser_context; }); + CHECK(it != g_context_to_user_id.Get().end()); + return it->second; +} BrowserContext::~BrowserContext() { -#if !defined(OS_IOS) + CHECK(GetUserData(kMojoWasInitialized)) + << "Attempting to destroy a BrowserContext that never called " + << "Initialize()"; + if (GetUserData(kDownloadManagerKeyName)) GetDownloadManager(this)->Shutdown(); -#endif } } // namespace content diff --git a/chromium/content/browser/browser_main.cc b/chromium/content/browser/browser_main.cc index d0897f1d650..459bde05a4d 100644 --- a/chromium/content/browser/browser_main.cc +++ b/chromium/content/browser/browser_main.cc @@ -4,6 +4,7 @@ #include "content/browser/browser_main.h" +#include "base/memory/scoped_ptr.h" #include "base/trace_event/trace_event.h" #include "content/common/content_constants_internal.h" #include "content/public/browser/browser_main_runner.h" diff --git a/chromium/content/browser/browser_main_loop.cc b/chromium/content/browser/browser_main_loop.cc index 63ee2cca5d9..175e9d69f7e 100644 --- a/chromium/content/browser/browser_main_loop.cc +++ b/chromium/content/browser/browser_main_loop.cc @@ -32,6 +32,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "components/tracing/process_metrics_memory_dump_provider.h" #include "components/tracing/trace_config_file.h" #include "components/tracing/trace_to_console.h" #include "components/tracing/tracing_switches.h" @@ -52,14 +53,17 @@ #include "content/browser/mojo/mojo_shell_context.h" #include "content/browser/net/browser_online_state_observer.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/speech/speech_recognition_manager_impl.h" #include "content/browser/startup_task_runner.h" #include "content/browser/time_zone_monitor.h" +#include "content/browser/utility_process_host_impl.h" #include "content/browser/webui/content_web_ui_controller_factory.h" #include "content/browser/webui/url_data_manager.h" #include "content/common/content_switches_internal.h" #include "content/common/host_discardable_shared_memory_manager.h" #include "content/common/host_shared_bitmap_manager.h" +#include "content/common/mojo/mojo_shell_connection_impl.h" #include "content/public/browser/browser_main_parts.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host.h" @@ -73,6 +77,8 @@ #include "media/base/media.h" #include "media/base/user_input_monitor.h" #include "media/midi/midi_manager.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/shell/public/cpp/shell.h" #include "net/base/network_change_notifier.h" #include "net/socket/client_socket_factory.h" #include "net/ssl/ssl_config_service.h" @@ -81,7 +87,7 @@ #include "sql/sql_memory_dump_provider.h" #include "ui/base/clipboard/clipboard.h" -#if defined(USE_AURA) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(USE_AURA) || defined(OS_MACOSX) #include "content/browser/compositor/image_transport_factory.h" #endif @@ -90,10 +96,6 @@ #include "ui/aura/env.h" #endif -#if !defined(OS_IOS) -#include "content/browser/renderer_host/render_process_host_impl.h" -#endif - #if defined(OS_ANDROID) #include "base/android/jni_android.h" #include "components/tracing/graphics_memory_dump_provider_android.h" @@ -106,18 +108,18 @@ #include "ui/gl/gl_surface.h" #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) #include "base/memory/memory_pressure_monitor_mac.h" #include "content/browser/bootstrap_sandbox_manager_mac.h" #include "content/browser/cocoa/system_hotkey_helper_mac.h" -#include "content/browser/compositor/browser_compositor_view_mac.h" +#include "content/browser/mach_broker_mac.h" +#include "content/browser/renderer_host/browser_compositor_view_mac.h" #include "content/browser/theme_helper_mac.h" #include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #endif #if defined(USE_OZONE) #include "ui/ozone/public/client_native_pixmap_factory.h" -#include "ui/ozone/public/ozone_platform.h" #endif #if defined(OS_WIN) @@ -126,7 +128,8 @@ #include #include "base/memory/memory_pressure_monitor_win.h" -#include "content/browser/system_message_window_win.h" +#include "base/win/windows_version.h" +#include "content/browser/screen_orientation/screen_orientation_delegate_win.h" #include "content/common/sandbox_win.h" #include "net/base/winsock_init.h" #include "ui/base/l10n/l10n_util_win.h" @@ -141,26 +144,29 @@ #include #endif -#if defined(OS_LINUX) && defined(USE_UDEV) -#include "content/browser/device_monitor_udev.h" -#elif defined(OS_MACOSX) && !defined(OS_IOS) -#include "content/browser/device_monitor_mac.h" +#if defined(OS_WIN) +#include "media/capture/system_message_window_win.h" +#elif defined(OS_LINUX) && defined(USE_UDEV) +#include "media/capture/device_monitor_udev.h" +#elif defined(OS_MACOSX) +#include "media/capture/device_monitor_mac.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "content/browser/renderer_host/render_sandbox_host_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" #include "sandbox/linux/suid/client/setuid_sandbox_host.h" -#endif + +#if !defined(OS_ANDROID) +#include "content/public/browser/zygote_handle_linux.h" +#endif // !defined(OS_ANDROID) +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + #if defined(ENABLE_PLUGINS) #include "content/browser/plugin_service_impl.h" #endif -#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) -#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" -#endif - #if defined(USE_X11) #include "ui/base/x/x11_util_internal.h" #include "ui/gfx/x/x11_connection.h" @@ -173,11 +179,7 @@ #endif #if defined(MOJO_SHELL_CLIENT) -#include "components/mus/public/interfaces/window_manager.mojom.h" -#include "content/common/mojo/mojo_shell_connection_impl.h" -#include "mojo/converters/network/network_type_converters.h" -#include "mojo/shell/public/cpp/application_impl.h" -#include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "mojo/shell/public/cpp/connector.h" #include "ui/views/mus/window_manager_connection.h" #endif @@ -220,6 +222,8 @@ void SetupSandbox(const base::CommandLine& parsed_command_line) { // Tickle the sandbox host and zygote host so they fork now. RenderSandboxHostLinux::GetInstance()->Init(); ZygoteHostImpl::GetInstance()->Init(sandbox_binary.value()); + *GetGenericZygote() = CreateZygote(); + RenderProcessHostImpl::EarlyZygoteLaunch(); } #endif @@ -332,13 +336,11 @@ NOINLINE void ResetThread_IO(scoped_ptr thread) { thread.reset(); } -#if !defined(OS_IOS) NOINLINE void ResetThread_IndexedDb(scoped_ptr thread) { volatile int inhibit_comdat = __LINE__; ALLOW_UNUSED_LOCAL(inhibit_comdat); thread.reset(); } -#endif MSVC_POP_WARNING() MSVC_ENABLE_OPTIMIZE(); @@ -383,13 +385,11 @@ class BrowserMainLoop::MemoryObserver : public base::MessageLoop::TaskObserver { void WillProcessTask(const base::PendingTask& pending_task) override {} void DidProcessTask(const base::PendingTask& pending_task) override { -#if !defined(OS_IOS) // No ProcessMetrics on IOS. scoped_ptr process_metrics( base::ProcessMetrics::CreateCurrentProcessMetrics()); size_t private_bytes; process_metrics->GetMemoryBytes(&private_bytes, NULL); LOCAL_HISTOGRAM_MEMORY_KB("Memory.BrowserUsed", private_bytes >> 10); -#endif } private: DISALLOW_COPY_AND_ASSIGN(MemoryObserver); @@ -420,9 +420,7 @@ BrowserMainLoop::BrowserMainLoop(const MainFunctionParams& parameters) BrowserMainLoop::~BrowserMainLoop() { DCHECK_EQ(this, g_current_browser_main_loop); -#if !defined(OS_IOS) ui::Clipboard::DestroyClipboardForCurrentThread(); -#endif // !defined(OS_IOS) g_current_browser_main_loop = NULL; } @@ -497,7 +495,6 @@ void BrowserMainLoop::EarlyInitialization() { crypto::EnsureNSPRInit(); #endif -#if !defined(OS_IOS) if (parsed_command_line_.HasSwitch(switches::kRendererProcessLimit)) { std::string limit_string = parsed_command_line_.GetSwitchValueASCII( switches::kRendererProcessLimit); @@ -506,7 +503,6 @@ void BrowserMainLoop::EarlyInitialization() { RenderProcessHost::SetMaxRendererProcessCount(process_limit); } } -#endif // !defined(OS_IOS) if (parts_) parts_->PostEarlyInitialization(); @@ -563,8 +559,6 @@ void BrowserMainLoop::PostMainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:NetworkChangeNotifier"); network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); } - -#if !defined(OS_IOS) { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MediaFeatures"); media::InitializeMediaLibrary(); @@ -586,10 +580,10 @@ void BrowserMainLoop::PostMainMessageLoopStart() { new base::trace_event::TraceEventSystemStatsMonitor( base::ThreadTaskRunnerHandle::Get())); } -#endif // !defined(OS_IOS) #if defined(OS_WIN) - system_message_window_.reset(new SystemMessageWindowWin); + if (base::win::GetVersion() >= base::win::VERSION_WIN8) + screen_orientation_delegate_.reset(new ScreenOrientationDelegateWin()); #endif // TODO(boliu): kSingleProcess check is a temporary workaround for @@ -622,7 +616,6 @@ void BrowserMainLoop::PostMainMessageLoopStart() { tracing::TraceConfigFile::GetInstance()->GetTraceConfig(), TracingController::StartTracingDoneCallback()); } -#if !defined(OS_IOS) // Start tracing to a file for certain duration if needed. Only do this after // starting the main message loop to avoid calling // MessagePumpForUI::ScheduleWork() before MessagePumpForUI::Start() as it @@ -631,20 +624,19 @@ void BrowserMainLoop::PostMainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::InitStartupTracingForDuration"); InitStartupTracingForDuration(parsed_command_line_); } -#endif // !defined(OS_IOS) #if defined(OS_ANDROID) { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SurfaceTextureManager"); if (parsed_command_line_.HasSwitch(switches::kSingleProcess)) { - SurfaceTextureManager::SetInstance( + gpu::SurfaceTextureManager::SetInstance( InProcessSurfaceTextureManager::GetInstance()); } else { - SurfaceTextureManager::SetInstance( + gpu::SurfaceTextureManager::SetInstance( BrowserSurfaceTextureManager::GetInstance()); } } -#if !defined(USE_AURA) + if (!parsed_command_line_.HasSwitch( switches::kDisableScreenOrientationLock)) { TRACE_EVENT0("startup", @@ -654,9 +646,8 @@ void BrowserMainLoop::PostMainMessageLoopStart() { ScreenOrientationProvider::SetDelegate(screen_orientation_delegate_.get()); } #endif -#endif -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) if (BootstrapSandboxManager::ShouldEnable()) { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:BootstrapSandbox"); @@ -668,8 +659,6 @@ void BrowserMainLoop::PostMainMessageLoopStart() { client_native_pixmap_factory_ = ui::ClientNativePixmapFactory::Create(); ui::ClientNativePixmapFactory::SetInstance( client_native_pixmap_factory_.get()); - ui::ClientNativePixmapFactory::GetInstance()->Initialize( - ui::OzonePlatform::GetInstance()->OpenClientNativePixmapDevice()); #endif if (parsed_command_line_.HasSwitch(switches::kMemoryMetrics)) { @@ -687,6 +676,8 @@ void BrowserMainLoop::PostMainMessageLoopStart() { // Enable memory-infra dump providers. InitSkiaEventTracer(); + tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess( + base::kNullProcessId); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( HostSharedBitmapManager::current(), "HostSharedBitmapManager", nullptr); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( @@ -704,9 +695,13 @@ int BrowserMainLoop::PreCreateThreads() { result_code_ = parts_->PreCreateThreads(); } - // Initialize an instance of FeatureList. This will be a no-op if an instance - // was already set up by the embedder. - base::FeatureList::InitializeInstance(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + // Note that we do not initialize a new FeatureList when calling this for + // the second time. + base::FeatureList::InitializeInstance( + command_line->GetSwitchValueASCII(switches::kEnableFeatures), + command_line->GetSwitchValueASCII(switches::kDisableFeatures)); // TODO(chrisha): Abstract away this construction mess to a helper function, // once MemoryPressureMonitor is made a concrete class. @@ -715,7 +710,7 @@ int BrowserMainLoop::PreCreateThreads() { memory_pressure_monitor_.reset(new base::chromeos::MemoryPressureMonitor( chromeos::switches::GetMemoryPressureThresholds())); } -#elif defined(OS_MACOSX) && !defined(OS_IOS) +#elif defined(OS_MACOSX) memory_pressure_monitor_.reset(new base::mac::MemoryPressureMonitor()); #elif defined(OS_WIN) memory_pressure_monitor_.reset(CreateWinMemoryPressureMonitor( @@ -733,7 +728,7 @@ int BrowserMainLoop::PreCreateThreads() { } #endif -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) // The WindowResizeHelper allows the UI thread to wait on specific renderer // and GPU messages from the IO thread. Initializing it before the IO thread // starts ensures the affected IO thread messages always have somewhere to go. @@ -746,7 +741,7 @@ int BrowserMainLoop::PreCreateThreads() { // 2) Must be after parts_->PreCreateThreads to pick up chrome://flags. GpuDataManagerImpl::GetInstance()->Initialize(); -#if !defined(OS_IOS) && (!defined(GOOGLE_CHROME_BUILD) || defined(OS_ANDROID)) +#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_ANDROID) // Single-process is an unsupported and not fully tested mode, so // don't enable it for official Chrome builds (except on Android). if (parsed_command_line_.HasSwitch(switches::kSingleProcess)) @@ -868,7 +863,9 @@ int BrowserMainLoop::CreateThreads() { "BrowserMainLoop::CreateThreads:start", "Thread", "BrowserThread::CACHE"); thread_to_start = &cache_thread_; +#if defined(OS_WIN) options = io_message_loop_options; +#endif // defined(OS_WIN) options.timer_slack = base::TIMER_SLACK_MAXIMUM; break; case BrowserThread::IO: @@ -877,7 +874,7 @@ int BrowserMainLoop::CreateThreads() { "Thread", "BrowserThread::IO"); thread_to_start = &io_thread_; options = io_message_loop_options; -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) // Up the priority of the |io_thread_| as some of its IPCs relate to // display tasks. options.priority = base::ThreadPriority::DISPLAY; @@ -908,17 +905,19 @@ int BrowserMainLoop::CreateThreads() { } int BrowserMainLoop::PreMainMessageLoopRun() { -#if defined(MOJO_SHELL_CLIENT) if (IsRunningInMojoShell()) { - mojo::embedder::PreInitializeChildProcess(); - MojoShellConnectionImpl::Create(); - MojoShellConnectionImpl::Get()->BindToCommandLinePlatformChannel(); -#if defined(USE_AURA) - views::WindowManagerConnection::Create( - MojoShellConnection::Get()->GetApplication()); + if (!MojoShellConnectionImpl::CreateUsingFactory()) { + mojo::edk::SetParentPipeHandleFromCommandLine(); + MojoShellConnectionImpl::Create(); + MojoShellConnectionImpl::Get()->BindToRequestFromCommandLine(); + } +#if defined(MOJO_SHELL_CLIENT) && defined(USE_AURA) + if (MojoShellConnection::Get()) { + views::WindowManagerConnection::Create( + MojoShellConnection::Get()->GetConnector()); + } #endif } -#endif if (parts_) { TRACE_EVENT0("startup", @@ -966,16 +965,11 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { base::Bind(base::IgnoreResult(&base::ThreadRestrictions::SetIOAllowed), true)); -#if defined(MOJO_SHELL_CLIENT) - MojoShellConnection::Destroy(); -#endif - mojo_ipc_support_.reset(); - mojo_shell_context_.reset(); + if (IsRunningInMojoShell()) + MojoShellConnection::Destroy(); -#if !defined(OS_IOS) if (RenderProcessHost::run_renderer_in_process()) RenderProcessHostImpl::ShutDownInProcessRenderer(); -#endif if (parts_) { TRACE_EVENT0("shutdown", @@ -989,7 +983,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { system_stats_monitor_.reset(); -#if !defined(OS_IOS) // Destroying the GpuProcessHostUIShims on the UI thread posts a task to // delete related objects on the GPU thread. This must be done before // stopping the GPU thread. The GPU thread will close IPC channels to renderer @@ -1034,7 +1027,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { #elif defined(OS_MACOSX) device_monitor_mac_.reset(); #endif -#endif // !defined(OS_IOS) + + // Shutdown Mojo shell and IPC. + mojo_shell_context_.reset(); + mojo_ipc_support_.reset(); // Must be size_t so we can subtract from it. for (size_t thread_id = BrowserThread::ID_COUNT - 1; @@ -1065,12 +1061,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { } case BrowserThread::FILE: { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread"); -#if !defined(OS_IOS) // Clean up state that lives on or uses the file_thread_ before // it goes away. if (resource_dispatcher_host_) resource_dispatcher_host_.get()->save_file_manager()->Shutdown(); -#endif // !defined(OS_IOS) ResetThread_FILE(std::move(file_thread_)); break; } @@ -1102,13 +1096,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { break; } } - -#if !defined(OS_IOS) { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IndexedDBThread"); ResetThread_IndexedDb(std::move(indexed_db_thread_)); } -#endif // Close the blocking I/O pool after the other threads. Other threads such // as the I/O thread may need to schedule work like closing files or flushing @@ -1120,8 +1111,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:ThreadPool"); BrowserThreadImpl::ShutdownThreadPool(); } - -#if !defined(OS_IOS) // Must happen after the IO thread is shutdown since this may be accessed from // it. { @@ -1150,7 +1139,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DeleteDataSources"); URLDataManager::DeleteDataSources(); } -#endif // !defined(OS_IOS) if (parts_) { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:PostDestroyThreads"); @@ -1177,21 +1165,28 @@ void BrowserMainLoop::InitializeMainThread() { int BrowserMainLoop::BrowserThreadsStarted() { TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted"); -#if !defined(OS_IOS) + // Bring up Mojo IPC and shell as early as possible. + mojo_ipc_support_.reset(new IPC::ScopedIPCSupport( + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) + ->task_runner())); + mojo_shell_context_.reset(new MojoShellContext(file_thread_->task_runner(), + db_thread_->task_runner())); +#if defined(OS_MACOSX) + mojo::edk::SetMachPortProvider(MachBroker::GetInstance()); +#endif // defined(OS_MACOSX) + indexed_db_thread_.reset(new base::Thread("IndexedDB")); indexed_db_thread_->Start(); -#endif -#if !defined(OS_IOS) HistogramSynchronizer::GetInstance(); -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) // Up the priority of the UI thread. base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif bool always_uses_gpu = true; bool established_gpu_channel = false; -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) // TODO(crbug.com/439322): This should be set to |true|. established_gpu_channel = false; BrowserGpuChannelHostFactory::Initialize(established_gpu_channel); @@ -1238,10 +1233,13 @@ int BrowserMainLoop::BrowserThreadsStarted() { midi_manager_.reset(media::midi::MidiManager::Create()); } -#if defined(OS_LINUX) && defined(USE_UDEV) - device_monitor_linux_.reset(new DeviceMonitorLinux()); +#if defined(OS_WIN) + system_message_window_.reset(new media::SystemMessageWindowWin); +#elif defined(OS_LINUX) && defined(USE_UDEV) + device_monitor_linux_.reset( + new media::DeviceMonitorLinux(io_thread_->task_runner())); #elif defined(OS_MACOSX) - device_monitor_mac_.reset(new DeviceMonitorMac()); + device_monitor_mac_.reset(new media::DeviceMonitorMac()); #endif #if defined(OS_WIN) @@ -1316,13 +1314,6 @@ int BrowserMainLoop::BrowserThreadsStarted() { SystemHotkeyHelperMac::GetInstance()->DeferredLoadSystemHotkeys(); #endif // defined(OS_MACOSX) -#endif // !defined(OS_IOS) - - mojo_shell_context_.reset(new MojoShellContext); - mojo_ipc_support_.reset(new IPC::ScopedIPCSupport( - BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) - ->task_runner())); - return result_code_; } @@ -1409,7 +1400,7 @@ base::FilePath BrowserMainLoop::GetStartupTraceFileName( return trace_file; if (trace_file.empty()) { -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) TracingControllerAndroid::GenerateTracingFilePath(&trace_file); #else // Default to saving the startup trace into the current dir. @@ -1417,7 +1408,7 @@ base::FilePath BrowserMainLoop::GetStartupTraceFileName( #endif } } else { -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) TracingControllerAndroid::GenerateTracingFilePath(&trace_file); #else trace_file = tracing::TraceConfigFile::GetInstance()->GetResultFile(); diff --git a/chromium/content/browser/browser_main_loop.h b/chromium/content/browser/browser_main_loop.h index ae07f764237..e8a13a03881 100644 --- a/chromium/content/browser/browser_main_loop.h +++ b/chromium/content/browser/browser_main_loop.h @@ -33,7 +33,15 @@ class ScopedIPCSupport; namespace media { class AudioManager; +#if defined(OS_WIN) +class SystemMessageWindowWin; +#elif defined(OS_LINUX) && defined(USE_UDEV) +class DeviceMonitorLinux; +#endif class UserInputMonitor; +#if defined(OS_MACOSX) +class DeviceMonitorMac; +#endif namespace midi { class MidiManager; } // namespace midi @@ -63,12 +71,8 @@ struct MainFunctionParams; #if defined(OS_ANDROID) class ScreenOrientationDelegate; -#elif defined(OS_LINUX) -class DeviceMonitorLinux; -#elif defined(OS_MACOSX) -class DeviceMonitorMac; #elif defined(OS_WIN) -class SystemMessageWindowWin; +class ScreenOrientationDelegate; #endif // Implements the main browser loop stages called from BrowserMainRunner. @@ -130,7 +134,7 @@ class CONTENT_EXPORT BrowserMainLoop { void StopStartupTracingTimer(); #if defined(OS_MACOSX) && !defined(OS_IOS) - DeviceMonitorMac* device_monitor_mac() const { + media::DeviceMonitorMac* device_monitor_mac() const { return device_monitor_mac_.get(); } #endif @@ -198,7 +202,7 @@ class CONTENT_EXPORT BrowserMainLoop { system_stats_monitor_; #if defined(OS_WIN) - scoped_ptr system_message_window_; + scoped_ptr screen_orientation_delegate_; #endif #if defined(OS_ANDROID) @@ -249,10 +253,12 @@ class CONTENT_EXPORT BrowserMainLoop { scoped_ptr midi_manager_; -#if defined(USE_UDEV) - scoped_ptr device_monitor_linux_; +#if defined(OS_WIN) + scoped_ptr system_message_window_; +#elif defined(OS_LINUX) && defined(USE_UDEV) + scoped_ptr device_monitor_linux_; #elif defined(OS_MACOSX) && !defined(OS_IOS) - scoped_ptr device_monitor_mac_; + scoped_ptr device_monitor_mac_; #endif #if defined(USE_OZONE) scoped_ptr client_native_pixmap_factory_; diff --git a/chromium/content/browser/browser_main_runner.cc b/chromium/content/browser/browser_main_runner.cc index 2585c8dd870..de130940eff 100644 --- a/chromium/content/browser/browser_main_runner.cc +++ b/chromium/content/browser/browser_main_runner.cc @@ -15,6 +15,7 @@ #include "base/profiler/scoped_profile.h" #include "base/profiler/scoped_tracker.h" #include "base/time/time.h" +#include "base/trace_event/heap_profiler_allocation_context_tracker.h" #include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" #include "build/build_config.h" @@ -34,12 +35,8 @@ #endif #if defined(OS_WIN) -#include "base/win/win_util.h" #include "base/win/windows_version.h" -#include "net/cert/sha256_legacy_support_win.h" -#include "sandbox/win/src/sidestep/preamble_patcher.h" #include "ui/base/win/scoped_ole_initializer.h" -#include "ui/gfx/switches.h" #include "ui/gfx/win/direct_write.h" #endif @@ -48,86 +45,7 @@ namespace content { namespace { bool g_exited_main_message_loop = false; - -#if defined(OS_WIN) -#if !defined(_WIN64) -// Pointer to the original CryptVerifyCertificateSignatureEx function. -net::sha256_interception::CryptVerifyCertificateSignatureExFunc - g_real_crypt_verify_signature_stub = NULL; - -// Stub function that is called whenever the Crypt32 function -// CryptVerifyCertificateSignatureEx is called. It just defers to net to perform -// the actual verification. -BOOL WINAPI CryptVerifyCertificateSignatureExStub( - HCRYPTPROV_LEGACY provider, - DWORD encoding_type, - DWORD subject_type, - void* subject_data, - DWORD issuer_type, - void* issuer_data, - DWORD flags, - void* extra) { - return net::sha256_interception::CryptVerifyCertificateSignatureExHook( - g_real_crypt_verify_signature_stub, provider, encoding_type, subject_type, - subject_data, issuer_type, issuer_data, flags, extra); -} -#endif // !defined(_WIN64) - -// If necessary, install an interception -void InstallSha256LegacyHooks() { -#if defined(_WIN64) - // Interception on x64 is not supported. - return; -#else - if (base::win::MaybeHasSHA256Support()) - return; - - net::sha256_interception::CryptVerifyCertificateSignatureExFunc - cert_verify_signature_ptr = reinterpret_cast< - net::sha256_interception::CryptVerifyCertificateSignatureExFunc>( - ::GetProcAddress(::GetModuleHandle(L"crypt32.dll"), - "CryptVerifyCertificateSignatureEx")); - CHECK(cert_verify_signature_ptr); - - DWORD old_protect = 0; - if (!::VirtualProtect(cert_verify_signature_ptr, 5, PAGE_EXECUTE_READWRITE, - &old_protect)) { - return; - } - - g_real_crypt_verify_signature_stub = - reinterpret_cast< - net::sha256_interception::CryptVerifyCertificateSignatureExFunc>( - VirtualAllocEx(::GetCurrentProcess(), NULL, - sidestep::kMaxPreambleStubSize, MEM_COMMIT, - PAGE_EXECUTE_READWRITE)); - if (g_real_crypt_verify_signature_stub == NULL) { - CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, - &old_protect)); - return; - } - - sidestep::SideStepError patch_result = - sidestep::PreamblePatcher::Patch( - cert_verify_signature_ptr, CryptVerifyCertificateSignatureExStub, - g_real_crypt_verify_signature_stub, sidestep::kMaxPreambleStubSize); - if (patch_result != sidestep::SIDESTEP_SUCCESS) { - CHECK(::VirtualFreeEx(::GetCurrentProcess(), - g_real_crypt_verify_signature_stub, 0, - MEM_RELEASE)); - CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, - &old_protect)); - return; - } - - DWORD dummy = 0; - CHECK(::VirtualProtect(cert_verify_signature_ptr, 5, old_protect, &dummy)); - CHECK(::VirtualProtect(g_real_crypt_verify_signature_stub, - sidestep::kMaxPreambleStubSize, old_protect, - &old_protect)); -#endif // _WIN64 -} -#endif // OS_WIN +const char kMainThreadName[] = "CrBrowserMain"; } // namespace @@ -147,7 +65,9 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { // TODO(vadimt, yiyaoliu): Remove all tracked_objects references below once // crbug.com/453640 is fixed. - tracked_objects::ThreadData::InitializeThreadContext("CrBrowserMain"); + tracked_objects::ThreadData::InitializeThreadContext(kMainThreadName); + base::trace_event::AllocationContextTracker::SetCurrentThreadName( + kMainThreadName); TRACK_SCOPED_REGION( "Startup", "BrowserMainRunnerImpl::Initialize"); TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); @@ -163,10 +83,8 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { SkGraphics::Init(); -#if !defined(OS_IOS) if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) base::debug::WaitForDebugger(60, true); -#endif #if defined(OS_WIN) if (base::win::GetVersion() < base::win::VERSION_VISTA) { @@ -180,7 +98,6 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { // Win32 API here directly. ImmDisableTextFrameService(static_cast(-1)); } - InstallSha256LegacyHooks(); #endif // OS_WIN base::StatisticsRecorder::Initialize(); @@ -266,7 +183,7 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled() && TracingController::GetInstance()->IsTracing()) { base::FilePath result_file; -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) TracingControllerAndroid::GenerateTracingFilePath(&result_file); #else result_file = tracing::TraceConfigFile::GetInstance()->GetResultFile(); diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc index 68899faf82a..274315ecd3b 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc @@ -105,9 +105,7 @@ void BrowserPluginEmbedder::ClearGuestDragStateIfApplicable() { // static bool BrowserPluginEmbedder::DidSendScreenRectsCallback( WebContents* guest_web_contents) { - RenderWidgetHostImpl::From( - guest_web_contents->GetRenderViewHost()->GetWidget()) - ->SendScreenRects(); + static_cast(guest_web_contents)->SendScreenRects(); // Not handled => Iterate over all guests. return false; } diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc index 33262a832e5..fd0c173ed0b 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc @@ -21,6 +21,7 @@ #include "content/browser/compositor/surface_utils.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/frame_host/render_frame_proxy_host.h" +#include "content/browser/frame_host/render_widget_host_view_child_frame.h" #include "content/browser/frame_host/render_widget_host_view_guest.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -119,9 +120,9 @@ int BrowserPluginGuest::GetGuestProxyRoutingID() { if (guest_proxy_routing_id_ != MSG_ROUTING_NONE) return guest_proxy_routing_id_; - // Create a swapped out RenderView for the guest in the embedder renderer + // Create a RenderFrameProxyHost for the guest in the embedder renderer // process, so that the embedder can access the guest's window object. - // On reattachment, we can reuse the same swapped out RenderView because + // On reattachment, we can reuse the same RenderFrameProxyHost because // the embedder process will always be the same even if the embedder // WebContents changes. // @@ -130,17 +131,12 @@ int BrowserPluginGuest::GetGuestProxyRoutingID() { // |guest_proxy_routing_id_| and perform any necessary cleanup on Detach // to enable this. SiteInstance* owner_site_instance = owner_web_contents_->GetSiteInstance(); - if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { - int proxy_routing_id = - GetWebContents()->GetFrameTree()->root()->render_manager()-> - CreateRenderFrameProxy(owner_site_instance); - guest_proxy_routing_id_ = RenderFrameProxyHost::FromID( - owner_site_instance->GetProcess()->GetID(), proxy_routing_id) - ->GetRenderViewHost()->GetRoutingID(); - } else { - guest_proxy_routing_id_ = - GetWebContents()->CreateSwappedOutRenderView(owner_site_instance); - } + int proxy_routing_id = + GetWebContents()->GetFrameTree()->root()->render_manager()-> + CreateRenderFrameProxy(owner_site_instance); + guest_proxy_routing_id_ = RenderFrameProxyHost::FromID( + owner_site_instance->GetProcess()->GetID(), proxy_routing_id) + ->GetRenderViewHost()->GetRoutingID(); return guest_proxy_routing_id_; } @@ -250,8 +246,6 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( bool handled = true; IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message) - IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CompositorFrameSwappedACK, - OnCompositorFrameSwappedACK) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Detach, OnDetach) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_DragStatusUpdate, OnDragStatusUpdate) @@ -264,8 +258,6 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ImeSetComposition, OnImeSetComposition) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_LockMouse_ACK, OnLockMouseAck) - IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ReclaimCompositorResources, - OnReclaimCompositorResources) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent, OnSetEditCommandsForNextKeyEvent) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus) @@ -397,24 +389,6 @@ void BrowserPluginGuest::PointerLockPermissionResponse(bool allow) { new BrowserPluginMsg_SetMouseLock(browser_plugin_instance_id(), allow)); } -// TODO(wjmaclean): Remove this once any remaining users of this pathway -// are gone. -void BrowserPluginGuest::SwapCompositorFrame( - uint32_t output_surface_id, - int host_process_id, - int host_routing_id, - scoped_ptr frame) { - last_pending_frame_.reset(new FrameMsg_CompositorFrameSwapped_Params()); - frame->AssignTo(&last_pending_frame_->frame); - last_pending_frame_->output_surface_id = output_surface_id; - last_pending_frame_->producing_route_id = host_routing_id; - last_pending_frame_->producing_host_id = host_process_id; - - SendMessageToEmbedder( - new BrowserPluginMsg_CompositorFrameSwapped( - browser_plugin_instance_id(), *last_pending_frame_)); -} - void BrowserPluginGuest::SetChildFrameSurface( const cc::SurfaceId& surface_id, const gfx::Size& frame_size, @@ -752,21 +726,6 @@ void BrowserPluginGuest::Attach( WebContentsImpl* embedder_web_contents, const BrowserPluginHostMsg_Attach_Params& params) { browser_plugin_instance_id_ = browser_plugin_instance_id; - // If a guest is detaching from one container and attaching to another - // container, then late arriving ACKs may be lost if the mapping from - // |browser_plugin_instance_id| to |guest_instance_id| changes. Thus we - // ensure that we always get new frames on attachment by ACKing the pending - // frame if it's still waiting on the ACK. - if (last_pending_frame_) { - cc::CompositorFrameAck ack; - RenderWidgetHostImpl::SendSwapCompositorFrameAck( - last_pending_frame_->producing_route_id, - last_pending_frame_->output_surface_id, - last_pending_frame_->producing_host_id, - ack); - last_pending_frame_.reset(); - } - // The guest is owned by the embedder. Attach is queued up so we cannot // change embedders before attach completes. If the embedder goes away, // so does the guest and so we will never call WillAttachComplete because @@ -781,16 +740,15 @@ void BrowserPluginGuest::Attach( void BrowserPluginGuest::OnWillAttachComplete( WebContentsImpl* embedder_web_contents, const BrowserPluginHostMsg_Attach_Params& params) { - bool use_cross_process_frames = - BrowserPluginGuestMode::UseCrossProcessFramesForGuests(); // If a RenderView has already been created for this new window, then we need // to initialize the browser-side state now so that the RenderFrameHostManager // does not create a new RenderView on navigation. - if (!use_cross_process_frames && has_render_view_) { + if (has_render_view_) { // This will trigger a callback to RenderViewReady after a round-trip IPC. static_cast(GetWebContents()->GetRenderViewHost()) ->GetWidget() ->Init(); + GetWebContents()->GetMainFrame()->Init(); WebContentsViewGuest* web_contents_view = static_cast(GetWebContents()->GetView()); if (!web_contents()->GetRenderViewHost()->GetWidget()->GetView()) { @@ -809,23 +767,11 @@ void BrowserPluginGuest::OnWillAttachComplete( RenderWidgetHostViewGuest* rwhv = static_cast( web_contents()->GetRenderWidgetHostView()); rwhv->RegisterSurfaceNamespaceId(); - - if (!use_cross_process_frames) - has_render_view_ = true; + has_render_view_ = true; RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Attached")); } -void BrowserPluginGuest::OnCompositorFrameSwappedACK( - int browser_plugin_instance_id, - const FrameHostMsg_CompositorFrameSwappedACK_Params& params) { - RenderWidgetHostImpl::SendSwapCompositorFrameAck(params.producing_route_id, - params.output_surface_id, - params.producing_host_id, - params.ack); - last_pending_frame_.reset(); -} - void BrowserPluginGuest::OnDetach(int browser_plugin_instance_id) { if (!attached()) return; @@ -834,8 +780,9 @@ void BrowserPluginGuest::OnDetach(int browser_plugin_instance_id) { // it's attached again. attached_ = false; - RenderWidgetHostViewGuest* rwhv = static_cast( - web_contents()->GetRenderWidgetHostView()); + RenderWidgetHostViewChildFrame* rwhv = + static_cast( + web_contents()->GetRenderWidgetHostView()); // If the guest is terminated, our host may already be gone. if (rwhv) rwhv->UnregisterSurfaceNamespaceId(); @@ -897,6 +844,7 @@ void BrowserPluginGuest::OnImeSetComposition( int selection_end) { Send(new InputMsg_ImeSetComposition(routing_id(), base::UTF8ToUTF16(text), underlines, + gfx::Range::InvalidRange(), selection_start, selection_end)); } @@ -920,15 +868,6 @@ void BrowserPluginGuest::OnExtendSelectionAndDelete( rfh->ExtendSelectionAndDelete(before, after); } -void BrowserPluginGuest::OnReclaimCompositorResources( - int browser_plugin_instance_id, - const FrameHostMsg_ReclaimCompositorResources_Params& params) { - RenderWidgetHostImpl::SendReclaimCompositorResources(params.route_id, - params.output_surface_id, - params.renderer_host_id, - params.ack); -} - void BrowserPluginGuest::OnLockMouse(bool user_gesture, bool last_unlocked_by_target, bool privileged) { @@ -973,6 +912,10 @@ void BrowserPluginGuest::OnSetEditCommandsForNextKeyEvent( void BrowserPluginGuest::OnSetVisibility(int browser_plugin_instance_id, bool visible) { + // For OOPIF-, the remote frame will handle visibility state. + if (BrowserPluginGuestMode::UseCrossProcessFramesForGuests()) + return; + guest_visible_ = visible; if (embedder_visible_ && guest_visible_) GetWebContents()->WasShown(); @@ -999,10 +942,7 @@ void BrowserPluginGuest::OnUpdateGeometry(int browser_plugin_instance_id, // The plugin has moved within the embedder without resizing or the // embedder/container's view rect changing. guest_window_rect_ = view_rect; - RenderWidgetHostImpl* rwh = RenderWidgetHostImpl::From( - GetWebContents()->GetRenderViewHost()->GetWidget()); - if (rwh) - rwh->SendScreenRects(); + GetWebContents()->SendScreenRects(); } void BrowserPluginGuest::OnHasTouchEventHandlers(bool accept) { diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.h b/chromium/content/browser/browser_plugin/browser_plugin_guest.h index 9cdc85e96d2..e6676f74d6d 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.h +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.h @@ -45,9 +45,6 @@ #include "ui/gfx/geometry/rect.h" struct BrowserPluginHostMsg_Attach_Params; -struct FrameHostMsg_CompositorFrameSwappedACK_Params; -struct FrameHostMsg_ReclaimCompositorResources_Params; -struct FrameMsg_CompositorFrameSwapped_Params; struct ViewHostMsg_TextInputState_Params; #if defined(OS_MACOSX) @@ -241,11 +238,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, void PointerLockPermissionResponse(bool allow); - // The next two functions are virtual for test purposes. - virtual void SwapCompositorFrame(uint32_t output_surface_id, - int host_process_id, - int host_routing_id, - scoped_ptr frame); + // The next function is virtual for test purposes. virtual void SetChildFrameSurface(const cc::SurfaceId& surface_id, const gfx::Size& frame_size, float scale_factor, @@ -289,9 +282,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, const cc::SurfaceId& id, const cc::SurfaceSequence& sequence); // Message handlers for messages from embedder. - void OnCompositorFrameSwappedACK( - int instance_id, - const FrameHostMsg_CompositorFrameSwappedACK_Params& params); void OnDetach(int instance_id); // Handles drag events from the embedder. // When dragging, the drag events go to the embedder first, and if the drag @@ -307,11 +297,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, void OnExecuteEditCommand(int instance_id, const std::string& command); - // Returns compositor resources reclaimed in the embedder to the guest. - void OnReclaimCompositorResources( - int instance_id, - const FrameHostMsg_ReclaimCompositorResources_Params& params); - void OnLockMouse(bool user_gesture, bool last_unlocked_by_target, bool privileged); @@ -456,15 +441,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, // Indicates the URL dragged into the guest if any. GURL dragged_url_; - // Guests generate frames and send a CompositorFrameSwapped (CFS) message - // indicating the next frame is ready to be positioned and composited. - // Subsequent frames are not generated until the IPC is ACKed. We would like - // to ensure that the guest generates frames on attachment so we directly ACK - // an unACKed CFS. ACKs could get lost between the time a guest is detached - // from a container and the time it is attached elsewhere. This mitigates this - // race by ensuring the guest is ACKed on attachment. - scoped_ptr last_pending_frame_; - // This is a queue of messages that are destined to be sent to the embedder // once the guest is attached to a particular embedder. std::deque > pending_messages_; diff --git a/chromium/content/browser/browser_process_sub_thread.cc b/chromium/content/browser/browser_process_sub_thread.cc index b1904e10971..5aadda5ad1e 100644 --- a/chromium/content/browser/browser_process_sub_thread.cc +++ b/chromium/content/browser/browser_process_sub_thread.cc @@ -66,12 +66,10 @@ void BrowserProcessSubThread::IOThreadPreCleanUp() { // Destroy all URLRequests started by URLFetchers. net::URLFetcher::CancelAll(); -#if !defined(OS_IOS) // If any child processes are still running, terminate them and // and delete the BrowserChildProcessHost instances to release whatever // IO thread only resources they are referencing. BrowserChildProcessHostImpl::TerminateAll(); -#endif // !defined(OS_IOS) // Unregister GpuMemoryBuffer dump provider before IO thread is shut down. base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( diff --git a/chromium/content/browser/browser_process_sub_thread.h b/chromium/content/browser/browser_process_sub_thread.h index 2276d6dca46..aded0cb883f 100644 --- a/chromium/content/browser/browser_process_sub_thread.h +++ b/chromium/content/browser/browser_process_sub_thread.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_BROWSER_PROCESS_SUB_THREAD_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "content/browser/browser_thread_impl.h" #include "content/common/content_export.h" diff --git a/chromium/content/browser/browser_side_navigation_browsertest.cc b/chromium/content/browser/browser_side_navigation_browsertest.cc index 3ffef7d88d2..599f3cc5b71 100644 --- a/chromium/content/browser/browser_side_navigation_browsertest.cc +++ b/chromium/content/browser/browser_side_navigation_browsertest.cc @@ -6,7 +6,9 @@ #include "base/command_line.h" #include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -156,9 +158,21 @@ IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, EXPECT_TRUE(observer.last_navigation_succeeded()); } - // The RenderFrameHost should not have changed. - EXPECT_EQ(initial_rfh, static_cast(shell()->web_contents()) - ->GetFrameTree()->root()->current_frame_host()); + // The RenderFrameHost should not have changed unless site-per-process is + // enabled. + if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { + EXPECT_NE(initial_rfh, + static_cast(shell()->web_contents()) + ->GetFrameTree() + ->root() + ->current_frame_host()); + } else { + EXPECT_EQ(initial_rfh, + static_cast(shell()->web_contents()) + ->GetFrameTree() + ->root() + ->current_frame_host()); + } } // Ensure that browser side navigation handles navigation failures. @@ -188,4 +202,30 @@ IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, FailedNavigation) { } } +// Ensure that browser side navigation handles POST navigations correctly. +IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, POSTNavigation) { + GURL url(embedded_test_server()->GetURL("/session_history/form.html")); + GURL post_url = embedded_test_server()->GetURL("/echotitle"); + + // Navigate to a page with a form. + TestNavigationObserver observer(shell()->web_contents()); + NavigateToURL(shell(), url); + EXPECT_EQ(url, observer.last_navigation_url()); + EXPECT_TRUE(observer.last_navigation_succeeded()); + + // Submit the form. + GURL submit_url("javascript:submitForm('isubmit')"); + NavigateToURL(shell(), submit_url); + + // Check that a proper POST navigation was done. + EXPECT_EQ("text=&select=a", + base::UTF16ToASCII(shell()->web_contents()->GetTitle())); + EXPECT_EQ(post_url, shell()->web_contents()->GetLastCommittedURL()); + EXPECT_TRUE(shell() + ->web_contents() + ->GetController() + .GetActiveEntry() + ->GetHasPostData()); +} + } // namespace content diff --git a/chromium/content/browser/browser_thread_impl.cc b/chromium/content/browser/browser_thread_impl.cc index b86643970e5..dd77c8dac04 100644 --- a/chromium/content/browser/browser_thread_impl.cc +++ b/chromium/content/browser/browser_thread_impl.cc @@ -13,9 +13,9 @@ #include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/macros.h" +#include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" #include "base/threading/sequenced_worker_pool.h" -#include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "content/public/browser/browser_thread_delegate.h" #include "content/public/browser/content_browser_client.h" @@ -230,10 +230,11 @@ void BrowserThreadImpl::Run(base::MessageLoop* message_loop) { #endif BrowserThread::ID thread_id = ID_COUNT; - if (!GetCurrentThreadIdentifier(&thread_id)) - return Thread::Run(message_loop); + CHECK(GetCurrentThreadIdentifier(&thread_id)); + CHECK_EQ(identifier_, thread_id); + CHECK_EQ(Thread::message_loop(), message_loop); - switch (thread_id) { + switch (identifier_) { case BrowserThread::UI: return UIThreadRun(message_loop); case BrowserThread::DB: @@ -252,7 +253,10 @@ void BrowserThreadImpl::Run(base::MessageLoop* message_loop) { CHECK(false); // This shouldn't actually be reached! break; } - Thread::Run(message_loop); + + // |identifier_| must be set to a valid enum value in the constructor, so it + // should be impossible to reach here. + CHECK(false); } void BrowserThreadImpl::CleanUp() { @@ -267,6 +271,13 @@ void BrowserThreadImpl::CleanUp() { if (delegate) delegate->CleanUp(); + + // PostTaskHelper() accesses the message loop while holding this lock. + // However, the message loop will soon be destructed without any locking. So + // to prevent a race with accessing the message loop in PostTaskHelper(), + // remove this thread from the global array now. + base::AutoLock lock(globals.lock); + globals.threads[identifier_] = NULL; } void BrowserThreadImpl::Initialize() { @@ -398,11 +409,6 @@ bool BrowserThread::IsThreadInitialized(ID identifier) { // static bool BrowserThread::CurrentlyOn(ID identifier) { - // We shouldn't use MessageLoop::current() since it uses LazyInstance which - // may be deleted by ~AtExitManager when a WorkerPool thread calls this - // function. - // http://crbug.com/63678 - base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; BrowserThreadGlobals& globals = g_globals.Get(); base::AutoLock lock(globals.lock); DCHECK(identifier >= 0 && identifier < ID_COUNT); @@ -501,13 +507,13 @@ bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { if (g_globals == NULL) return false; - // We shouldn't use MessageLoop::current() since it uses LazyInstance which - // may be deleted by ~AtExitManager when a WorkerPool thread calls this - // function. - // http://crbug.com/63678 - base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; base::MessageLoop* cur_message_loop = base::MessageLoop::current(); BrowserThreadGlobals& globals = g_globals.Get(); + // Profiler to track potential contention on |globals.lock|. This only does + // real work on canary and local dev builds, so the cost of having this here + // should be minimal. + tracked_objects::ScopedTracker tracking_profile(FROM_HERE); + base::AutoLock lock(globals.lock); for (int i = 0; i < ID_COUNT; ++i) { if (globals.threads[i] && globals.threads[i]->message_loop() == cur_message_loop) { diff --git a/chromium/content/browser/browsing_instance.cc b/chromium/content/browser/browsing_instance.cc index 1d5f8953769..5dd0ca4191f 100644 --- a/chromium/content/browser/browsing_instance.cc +++ b/chromium/content/browser/browsing_instance.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "content/browser/site_instance_impl.h" +#include "content/common/site_isolation_policy.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_switches.h" @@ -27,29 +28,49 @@ bool BrowsingInstance::HasSiteInstance(const GURL& url) { return site_instance_map_.find(site) != site_instance_map_.end(); } -SiteInstance* BrowsingInstance::GetSiteInstanceForURL(const GURL& url) { - std::string site = - SiteInstanceImpl::GetSiteForURL(browser_context_, url) - .possibly_invalid_spec(); +scoped_refptr BrowsingInstance::GetSiteInstanceForURL( + const GURL& url) { + std::string site = SiteInstanceImpl::GetSiteForURL(browser_context_, url) + .possibly_invalid_spec(); SiteInstanceMap::iterator i = site_instance_map_.find(site); if (i != site_instance_map_.end()) return i->second; - // No current SiteInstance for this site, so let's create one. - SiteInstanceImpl* instance = new SiteInstanceImpl(this); + scoped_refptr instance = new SiteInstanceImpl(this); // Set the site of this new SiteInstance, which will register it with us. instance->SetSite(url); return instance; } -void BrowsingInstance::RegisterSiteInstance(SiteInstance* site_instance) { - DCHECK(static_cast(site_instance) - ->browsing_instance_.get() == - this); - DCHECK(static_cast(site_instance)->HasSite()); +scoped_refptr +BrowsingInstance::GetDefaultSubframeSiteInstance() { + // This should only be used for --top-document-isolation mode. + CHECK(SiteIsolationPolicy::IsTopDocumentIsolationEnabled()); + if (!default_subframe_site_instance_) { + SiteInstanceImpl* instance = new SiteInstanceImpl(this); + instance->set_is_default_subframe_site_instance(); + + // TODO(nick): This is a hack for now. + instance->SetSite(GURL("http://web-subframes.invalid")); + + default_subframe_site_instance_ = instance; + } + + return make_scoped_refptr(default_subframe_site_instance_); +} + +void BrowsingInstance::RegisterSiteInstance(SiteInstanceImpl* site_instance) { + DCHECK(site_instance->browsing_instance_.get() == this); + DCHECK(site_instance->HasSite()); + + // Don't register the default subframe SiteInstance, to prevent it from being + // returned by GetSiteInstanceForURL. + if (default_subframe_site_instance_ == site_instance) + return; + std::string site = site_instance->GetSiteURL().possibly_invalid_spec(); // Only register if we don't have a SiteInstance for this site already. @@ -64,11 +85,9 @@ void BrowsingInstance::RegisterSiteInstance(SiteInstance* site_instance) { } } -void BrowsingInstance::UnregisterSiteInstance(SiteInstance* site_instance) { - DCHECK(static_cast(site_instance) - ->browsing_instance_.get() == - this); - DCHECK(static_cast(site_instance)->HasSite()); +void BrowsingInstance::UnregisterSiteInstance(SiteInstanceImpl* site_instance) { + DCHECK(site_instance->browsing_instance_.get() == this); + DCHECK(site_instance->HasSite()); std::string site = site_instance->GetSiteURL().possibly_invalid_spec(); // Only unregister the SiteInstance if it is the same one that is registered @@ -79,6 +98,8 @@ void BrowsingInstance::UnregisterSiteInstance(SiteInstance* site_instance) { // Matches, so erase it. site_instance_map_.erase(i); } + if (default_subframe_site_instance_ == site_instance) + default_subframe_site_instance_ = nullptr; } BrowsingInstance::~BrowsingInstance() { diff --git a/chromium/content/browser/browsing_instance.h b/chromium/content/browser/browsing_instance.h index 0cb80ac80a9..34c1f073bbd 100644 --- a/chromium/content/browser/browsing_instance.h +++ b/chromium/content/browser/browsing_instance.h @@ -8,6 +8,7 @@ #include #include "base/containers/hash_tables.h" +#include "base/gtest_prod_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" @@ -18,7 +19,6 @@ class GURL; namespace content { -class SiteInstance; class SiteInstanceImpl; /////////////////////////////////////////////////////////////////////////////// @@ -55,12 +55,20 @@ class SiteInstanceImpl; // site_instance_unittest.cc. // /////////////////////////////////////////////////////////////////////////////// -class CONTENT_EXPORT BrowsingInstance +class CONTENT_EXPORT BrowsingInstance final : public base::RefCounted { - protected: + private: + friend class base::RefCounted; + friend class SiteInstanceImpl; + FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest, OneSiteInstancePerSite); + FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest, + OneSiteInstancePerSiteInBrowserContext); + // Create a new BrowsingInstance. explicit BrowsingInstance(BrowserContext* context); + ~BrowsingInstance(); + // Get the browser context to which this BrowsingInstance belongs. BrowserContext* browser_context() const { return browser_context_; } @@ -71,17 +79,23 @@ class CONTENT_EXPORT BrowsingInstance // Get the SiteInstance responsible for rendering the given URL. Should // create a new one if necessary, but should not create more than one // SiteInstance per site. - SiteInstance* GetSiteInstanceForURL(const GURL& url); + scoped_refptr GetSiteInstanceForURL(const GURL& url); + + // Returns a SiteInstance that should be used for subframes when an oopif is + // required, but a dedicated process is not. This SiteInstance will be created + // if it doesn't already exist. There is at most one of these per + // BrowsingInstance. + scoped_refptr GetDefaultSubframeSiteInstance(); // Adds the given SiteInstance to our map, to ensure that we do not create // another SiteInstance for the same site. - void RegisterSiteInstance(SiteInstance* site_instance); + void RegisterSiteInstance(SiteInstanceImpl* site_instance); // Removes the given SiteInstance from our map, after all references to it // have been deleted. This means it is safe to create a new SiteInstance // if the user later visits a page from this site, within this // BrowsingInstance. - void UnregisterSiteInstance(SiteInstance* site_instance); + void UnregisterSiteInstance(SiteInstanceImpl* site_instance); // Tracks the number of WebContents currently in this BrowsingInstance. size_t active_contents_count() const { return active_contents_count_; } @@ -91,18 +105,9 @@ class CONTENT_EXPORT BrowsingInstance active_contents_count_--; } - friend class SiteInstanceImpl; - friend class SiteInstance; - - friend class base::RefCounted; - - // Virtual to allow tests to extend it. - virtual ~BrowsingInstance(); - - private: // Map of site to SiteInstance, to ensure we only have one SiteInstance per // site. - typedef base::hash_map SiteInstanceMap; + typedef base::hash_map SiteInstanceMap; // Common browser context to which all SiteInstances in this BrowsingInstance // must belong. @@ -120,6 +125,8 @@ class CONTENT_EXPORT BrowsingInstance // Number of WebContentses currently using this BrowsingInstance. size_t active_contents_count_; + SiteInstanceImpl* default_subframe_site_instance_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(BrowsingInstance); }; diff --git a/chromium/content/browser/cache_storage/cache_storage.cc b/chromium/content/browser/cache_storage/cache_storage.cc index 362af37e58c..c5a7b1eff63 100644 --- a/chromium/content/browser/cache_storage/cache_storage.cc +++ b/chromium/content/browser/cache_storage/cache_storage.cc @@ -46,28 +46,45 @@ std::string HexedHash(const std::string& value) { return valued_hexed_hash; } -void CloseAllCachesDidCloseCache(const scoped_refptr& cache, - const base::Closure& barrier_closure) { - barrier_closure.Run(); +void SizeRetrievedFromCache(const scoped_refptr& cache, + const base::Closure& closure, + int64_t* accumulator, + int64_t size) { + *accumulator += size; + closure.Run(); +} + +void SizeRetrievedFromAllCaches(scoped_ptr accumulator, + const CacheStorage::SizeCallback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, *accumulator)); } } // namespace const char CacheStorage::kIndexFileName[] = "index.txt"; +struct CacheStorage::CacheMatchResponse { + CacheMatchResponse() = default; + ~CacheMatchResponse() = default; + + CacheStorageError error; + scoped_ptr service_worker_response; + scoped_ptr blob_data_handle; +}; + // Handles the loading and clean up of CacheStorageCache objects. class CacheStorage::CacheLoader { public: - typedef base::Callback&)> - CacheCallback; + typedef base::Callback)> CacheCallback; typedef base::Callback BoolCallback; typedef base::Callback>)> StringVectorCallback; CacheLoader( base::SequencedTaskRunner* cache_task_runner, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + storage::QuotaManagerProxy* quota_manager_proxy, base::WeakPtr blob_context, const GURL& origin) : cache_task_runner_(cache_task_runner), @@ -105,7 +122,10 @@ class CacheStorage::CacheLoader { protected: scoped_refptr cache_task_runner_; scoped_refptr request_context_getter_; - scoped_refptr quota_manager_proxy_; + + // Owned by CacheStorage which owns this. + storage::QuotaManagerProxy* quota_manager_proxy_; + base::WeakPtr blob_context_; GURL origin_; }; @@ -116,12 +136,11 @@ class CacheStorage::CacheLoader { // cache is deleted. class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader { public: - MemoryLoader( - base::SequencedTaskRunner* cache_task_runner, - const scoped_refptr& request_context, - const scoped_refptr& quota_manager_proxy, - base::WeakPtr blob_context, - const GURL& origin) + MemoryLoader(base::SequencedTaskRunner* cache_task_runner, + scoped_refptr request_context, + storage::QuotaManagerProxy* quota_manager_proxy, + base::WeakPtr blob_context, + const GURL& origin) : CacheLoader(cache_task_runner, request_context, quota_manager_proxy, @@ -131,14 +150,15 @@ class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader { scoped_refptr CreateCache( const std::string& cache_name) override { return CacheStorageCache::CreateMemoryCache( - origin_, request_context_getter_, quota_manager_proxy_, blob_context_); + origin_, cache_name, request_context_getter_, quota_manager_proxy_, + blob_context_); } void PrepareNewCacheDestination(const std::string& cache_name, const CacheCallback& callback) override { scoped_refptr cache = CreateCache(cache_name); cache_refs_.insert(std::make_pair(cache_name, cache)); - callback.Run(cache); + callback.Run(std::move(cache)); } void CleanUpDeletedCache(const std::string& cache_name, @@ -171,13 +191,12 @@ class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader { class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { public: - SimpleCacheLoader( - const base::FilePath& origin_path, - base::SequencedTaskRunner* cache_task_runner, - const scoped_refptr& request_context, - const scoped_refptr& quota_manager_proxy, - base::WeakPtr blob_context, - const GURL& origin) + SimpleCacheLoader(const base::FilePath& origin_path, + base::SequencedTaskRunner* cache_task_runner, + scoped_refptr request_context, + storage::QuotaManagerProxy* quota_manager_proxy, + base::WeakPtr blob_context, + const GURL& origin) : CacheLoader(cache_task_runner, request_context, quota_manager_proxy, @@ -194,8 +213,8 @@ class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { std::string cache_dir = cache_name_to_cache_dir_[cache_name]; base::FilePath cache_path = origin_path_.AppendASCII(cache_dir); return CacheStorageCache::CreatePersistentCache( - origin_, cache_path, request_context_getter_, quota_manager_proxy_, - blob_context_); + origin_, cache_name, cache_path, request_context_getter_, + quota_manager_proxy_, blob_context_); } void PrepareNewCacheDestination(const std::string& cache_name, @@ -253,7 +272,7 @@ class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { static void CleanUpDeleteCacheDirInPool( const base::FilePath& cache_path, const BoolCallback& callback, - const scoped_refptr& original_task_runner) { + scoped_refptr original_task_runner) { bool rv = base::DeleteFile(cache_path, true); original_task_runner->PostTask(FROM_HERE, base::Bind(callback, rv)); } @@ -284,27 +303,24 @@ class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { base::FilePath index_path = origin_path_.AppendASCII(CacheStorage::kIndexFileName); - cache_task_runner_->PostTask( - FROM_HERE, base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool, - tmp_path, index_path, serialized, callback, - base::ThreadTaskRunnerHandle::Get())); + PostTaskAndReplyWithResult( + cache_task_runner_.get(), FROM_HERE, + base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool, tmp_path, + index_path, serialized), + callback); } - static void WriteIndexWriteToFileInPool( - const base::FilePath& tmp_path, - const base::FilePath& index_path, - const std::string& data, - const BoolCallback& callback, - const scoped_refptr& original_task_runner) { + static bool WriteIndexWriteToFileInPool(const base::FilePath& tmp_path, + const base::FilePath& index_path, + const std::string& data) { int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size()); if (bytes_written != base::checked_cast(data.size())) { base::DeleteFile(tmp_path, /* recursive */ false); - original_task_runner->PostTask(FROM_HERE, base::Bind(callback, false)); + return false; } // Atomically rename the temporary index file to become the real one. - bool rv = base::ReplaceFile(tmp_path, index_path, NULL); - original_task_runner->PostTask(FROM_HERE, base::Bind(callback, rv)); + return base::ReplaceFile(tmp_path, index_path, NULL); } void LoadIndex(scoped_ptr> names, @@ -442,8 +458,8 @@ CacheStorage::CacheStorage( const base::FilePath& path, bool memory_only, base::SequencedTaskRunner* cache_task_runner, - const scoped_refptr& request_context, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context, const GURL& origin) : initialized_(false), @@ -452,15 +468,17 @@ CacheStorage::CacheStorage( origin_path_(path), cache_task_runner_(cache_task_runner), memory_only_(memory_only), + quota_manager_proxy_(quota_manager_proxy), + origin_(origin), weak_factory_(this) { if (memory_only) - cache_loader_.reset(new MemoryLoader(cache_task_runner_.get(), - request_context, quota_manager_proxy, - blob_context, origin)); + cache_loader_.reset( + new MemoryLoader(cache_task_runner_.get(), std::move(request_context), + quota_manager_proxy.get(), blob_context, origin)); else cache_loader_.reset(new SimpleCacheLoader( - origin_path_, cache_task_runner_.get(), request_context, - quota_manager_proxy, blob_context, origin)); + origin_path_, cache_task_runner_.get(), std::move(request_context), + quota_manager_proxy.get(), blob_context, origin)); } CacheStorage::~CacheStorage() { @@ -473,6 +491,10 @@ void CacheStorage::OpenCache(const std::string& cache_name, if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + CacheAndErrorCallback pending_callback = base::Bind(&CacheStorage::PendingCacheAndErrorCallback, weak_factory_.GetWeakPtr(), callback); @@ -488,6 +510,10 @@ void CacheStorage::HasCache(const std::string& cache_name, if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + BoolAndErrorCallback pending_callback = base::Bind(&CacheStorage::PendingBoolAndErrorCallback, weak_factory_.GetWeakPtr(), callback); @@ -503,6 +529,10 @@ void CacheStorage::DeleteCache(const std::string& cache_name, if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + BoolAndErrorCallback pending_callback = base::Bind(&CacheStorage::PendingBoolAndErrorCallback, weak_factory_.GetWeakPtr(), callback); @@ -517,6 +547,10 @@ void CacheStorage::EnumerateCaches(const StringsAndErrorCallback& callback) { if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + StringsAndErrorCallback pending_callback = base::Bind(&CacheStorage::PendingStringsAndErrorCallback, weak_factory_.GetWeakPtr(), callback); @@ -534,6 +568,10 @@ void CacheStorage::MatchCache( if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + CacheStorageCache::ResponseCallback pending_callback = base::Bind(&CacheStorage::PendingResponseCallback, weak_factory_.GetWeakPtr(), callback); @@ -550,6 +588,10 @@ void CacheStorage::MatchAllCaches( if (!initialized_) LazyInit(); + quota_manager_proxy_->NotifyStorageAccessed( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary); + CacheStorageCache::ResponseCallback pending_callback = base::Bind(&CacheStorage::PendingResponseCallback, weak_factory_.GetWeakPtr(), callback); @@ -558,33 +600,31 @@ void CacheStorage::MatchAllCaches( base::Passed(std::move(request)), pending_callback)); } -void CacheStorage::CloseAllCaches(const base::Closure& callback) { +void CacheStorage::GetSizeThenCloseAllCaches(const SizeCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!initialized_) { - callback.Run(); - return; - } + if (!initialized_) + LazyInit(); - base::Closure pending_callback = base::Bind( - &CacheStorage::PendingClosure, weak_factory_.GetWeakPtr(), callback); - scheduler_->ScheduleOperation(base::Bind(&CacheStorage::CloseAllCachesImpl, - weak_factory_.GetWeakPtr(), - pending_callback)); + CacheStorageCache::SizeCallback pending_callback = base::Bind( + &CacheStorage::PendingSizeCallback, weak_factory_.GetWeakPtr(), callback); + + scheduler_->ScheduleOperation( + base::Bind(&CacheStorage::GetSizeThenCloseAllCachesImpl, + weak_factory_.GetWeakPtr(), pending_callback)); } -int64_t CacheStorage::MemoryBackedSize() const { +void CacheStorage::Size(const CacheStorage::SizeCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!initialized_ || !memory_only_) - return 0; + if (!initialized_) + LazyInit(); + + CacheStorageCache::SizeCallback pending_callback = base::Bind( + &CacheStorage::PendingSizeCallback, weak_factory_.GetWeakPtr(), callback); - int64_t sum = 0; - for (auto& key_value : cache_map_) { - if (key_value.second) - sum += key_value.second->MemoryBackedSize(); - } - return sum; + scheduler_->ScheduleOperation(base::Bind( + &CacheStorage::SizeImpl, weak_factory_.GetWeakPtr(), pending_callback)); } void CacheStorage::StartAsyncOperationForTesting() { @@ -648,7 +688,7 @@ void CacheStorage::OpenCacheImpl(const std::string& cache_name, const CacheAndErrorCallback& callback) { scoped_refptr cache = GetLoadedCache(cache_name); if (cache.get()) { - callback.Run(cache, CACHE_STORAGE_OK); + callback.Run(std::move(cache), CACHE_STORAGE_OK); return; } @@ -660,7 +700,7 @@ void CacheStorage::OpenCacheImpl(const std::string& cache_name, void CacheStorage::CreateCacheDidCreateCache( const std::string& cache_name, const CacheAndErrorCallback& callback, - const scoped_refptr& cache) { + scoped_refptr cache) { DCHECK_CURRENTLY_ON(BrowserThread::IO); UMA_HISTOGRAM_BOOLEAN("ServiceWorkerCache.CreateCacheStorageResult", @@ -680,19 +720,19 @@ void CacheStorage::CreateCacheDidCreateCache( cache_loader_->WriteIndex( ordered_cache_names_, base::Bind(&CacheStorage::CreateCacheDidWriteIndex, - weak_factory_.GetWeakPtr(), callback, cache)); + weak_factory_.GetWeakPtr(), callback, std::move(cache))); } void CacheStorage::CreateCacheDidWriteIndex( const CacheAndErrorCallback& callback, - const scoped_refptr& cache, + scoped_refptr cache, bool success) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(cache.get()); // TODO(jkarlin): Handle !success. - callback.Run(cache, CACHE_STORAGE_OK); + callback.Run(std::move(cache), CACHE_STORAGE_OK); } void CacheStorage::HasCacheImpl(const std::string& cache_name, @@ -703,14 +743,15 @@ void CacheStorage::HasCacheImpl(const std::string& cache_name, void CacheStorage::DeleteCacheImpl(const std::string& cache_name, const BoolAndErrorCallback& callback) { - CacheMap::iterator it = cache_map_.find(cache_name); - if (it == cache_map_.end()) { - callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND); + scoped_refptr cache = GetLoadedCache(cache_name); + if (!cache.get()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, false, CACHE_STORAGE_ERROR_NOT_FOUND)); return; } - base::WeakPtr cache = it->second; - cache_map_.erase(it); + CacheMap::iterator map_iter = cache_map_.find(cache_name); + cache_map_.erase(map_iter); // Delete the name from ordered_cache_names_. StringVector::iterator iter = std::find( @@ -718,36 +759,33 @@ void CacheStorage::DeleteCacheImpl(const std::string& cache_name, DCHECK(iter != ordered_cache_names_.end()); ordered_cache_names_.erase(iter); - base::Closure closure = - base::Bind(&CacheStorage::DeleteCacheDidClose, weak_factory_.GetWeakPtr(), - cache_name, callback, ordered_cache_names_, - make_scoped_refptr(cache.get())); - - if (cache) { - cache->Close(closure); - return; - } - - closure.Run(); + cache->GetSizeThenClose(base::Bind(&CacheStorage::DeleteCacheDidClose, + weak_factory_.GetWeakPtr(), cache_name, + callback, ordered_cache_names_, cache)); } -void CacheStorage::DeleteCacheDidClose( - const std::string& cache_name, - const BoolAndErrorCallback& callback, - const StringVector& ordered_cache_names, - const scoped_refptr& cache /* might be null */) { +void CacheStorage::DeleteCacheDidClose(const std::string& cache_name, + const BoolAndErrorCallback& callback, + const StringVector& ordered_cache_names, + scoped_refptr cache, + int64_t cache_size) { cache_loader_->WriteIndex( ordered_cache_names, base::Bind(&CacheStorage::DeleteCacheDidWriteIndex, - weak_factory_.GetWeakPtr(), cache_name, callback)); + weak_factory_.GetWeakPtr(), cache_name, callback, cache_size)); } void CacheStorage::DeleteCacheDidWriteIndex( const std::string& cache_name, const BoolAndErrorCallback& callback, + int cache_size, bool success) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + quota_manager_proxy_->NotifyStorageModified( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary, -1 * cache_size); + cache_loader_->CleanUpDeletedCache( cache_name, base::Bind(&CacheStorage::DeleteCacheDidCleanUp, weak_factory_.GetWeakPtr(), callback)); @@ -772,7 +810,7 @@ void CacheStorage::MatchCacheImpl( scoped_refptr cache = GetLoadedCache(cache_name); if (!cache.get()) { - callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND, + callback.Run(CACHE_STORAGE_ERROR_CACHE_NAME_NOT_FOUND, scoped_ptr(), scoped_ptr()); return; @@ -786,7 +824,7 @@ void CacheStorage::MatchCacheImpl( } void CacheStorage::MatchCacheDidMatch( - const scoped_refptr& cache, + scoped_refptr cache, const CacheStorageCache::ResponseCallback& callback, CacheStorageError error, scoped_ptr response, @@ -797,51 +835,55 @@ void CacheStorage::MatchCacheDidMatch( void CacheStorage::MatchAllCachesImpl( scoped_ptr request, const CacheStorageCache::ResponseCallback& callback) { - scoped_ptr callback_copy( - new CacheStorageCache::ResponseCallback(callback)); - - CacheStorageCache::ResponseCallback* callback_ptr = callback_copy.get(); - base::Closure barrier_closure = - base::BarrierClosure(ordered_cache_names_.size(), - base::Bind(&CacheStorage::MatchAllCachesDidMatchAll, - weak_factory_.GetWeakPtr(), - base::Passed(std::move(callback_copy)))); - - for (const std::string& cache_name : ordered_cache_names_) { - scoped_refptr cache = GetLoadedCache(cache_name); + std::vector* match_responses = + new std::vector(ordered_cache_names_.size()); + + base::Closure barrier_closure = base::BarrierClosure( + ordered_cache_names_.size(), + base::Bind(&CacheStorage::MatchAllCachesDidMatchAll, + weak_factory_.GetWeakPtr(), + base::Passed(make_scoped_ptr(match_responses)), callback)); + + for (size_t i = 0, max = ordered_cache_names_.size(); i < max; ++i) { + scoped_refptr cache = + GetLoadedCache(ordered_cache_names_[i]); DCHECK(cache.get()); cache->Match(make_scoped_ptr(new ServiceWorkerFetchRequest(*request)), base::Bind(&CacheStorage::MatchAllCachesDidMatch, - weak_factory_.GetWeakPtr(), cache, barrier_closure, - callback_ptr)); + weak_factory_.GetWeakPtr(), cache, + &match_responses->at(i), barrier_closure)); } } void CacheStorage::MatchAllCachesDidMatch( scoped_refptr cache, + CacheMatchResponse* out_match_response, const base::Closure& barrier_closure, - CacheStorageCache::ResponseCallback* callback, CacheStorageError error, - scoped_ptr response, + scoped_ptr service_worker_response, scoped_ptr handle) { - if (callback->is_null() || error == CACHE_STORAGE_ERROR_NOT_FOUND) { - barrier_closure.Run(); - return; - } - callback->Run(error, std::move(response), std::move(handle)); - callback->Reset(); // Only call the callback once. - + out_match_response->error = error; + out_match_response->service_worker_response = + std::move(service_worker_response); + out_match_response->blob_data_handle = std::move(handle); barrier_closure.Run(); } void CacheStorage::MatchAllCachesDidMatchAll( - scoped_ptr callback) { - if (!callback->is_null()) { - callback->Run(CACHE_STORAGE_ERROR_NOT_FOUND, - scoped_ptr(), - scoped_ptr()); + scoped_ptr> match_responses, + const CacheStorageCache::ResponseCallback& callback) { + for (CacheMatchResponse& match_response : *match_responses) { + if (match_response.error == CACHE_STORAGE_ERROR_NOT_FOUND) + continue; + callback.Run(match_response.error, + std::move(match_response.service_worker_response), + std::move(match_response.blob_data_handle)); + return; } + callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND, + scoped_ptr(), + scoped_ptr()); } scoped_refptr CacheStorage::GetLoadedCache( @@ -868,7 +910,7 @@ scoped_refptr CacheStorage::GetLoadedCache( } void CacheStorage::TemporarilyPreserveCache( - const scoped_refptr& cache) { + scoped_refptr cache) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!ContainsKey(preserved_caches_, cache.get())); @@ -894,32 +936,42 @@ void CacheStorage::RemovePreservedCache(const CacheStorageCache* cache) { preserved_caches_.erase(cache); } -void CacheStorage::CloseAllCachesImpl(const base::Closure& callback) { - int live_cache_count = 0; - for (const auto& key_value : cache_map_) { - if (key_value.second) - live_cache_count += 1; - } +void CacheStorage::GetSizeThenCloseAllCachesImpl(const SizeCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(initialized_); - if (live_cache_count == 0) { - callback.Run(); - return; - } + scoped_ptr accumulator(new int64_t(0)); + int64_t* accumulator_ptr = accumulator.get(); - // The closure might modify this object so delay calling it until after - // iterating through cache_map_ by adding one to the barrier. - base::Closure barrier_closure = - base::BarrierClosure(live_cache_count + 1, base::Bind(callback)); + base::Closure barrier_closure = base::BarrierClosure( + ordered_cache_names_.size(), + base::Bind(&SizeRetrievedFromAllCaches, + base::Passed(std::move(accumulator)), callback)); - for (auto& key_value : cache_map_) { - if (key_value.second) { - key_value.second->Close(base::Bind( - CloseAllCachesDidCloseCache, - make_scoped_refptr(key_value.second.get()), barrier_closure)); - } + for (const std::string& cache_name : ordered_cache_names_) { + scoped_refptr cache = GetLoadedCache(cache_name); + cache->GetSizeThenClose(base::Bind(&SizeRetrievedFromCache, cache, + barrier_closure, accumulator_ptr)); } +} - barrier_closure.Run(); +void CacheStorage::SizeImpl(const SizeCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(initialized_); + + scoped_ptr accumulator(new int64_t(0)); + int64_t* accumulator_ptr = accumulator.get(); + + base::Closure barrier_closure = base::BarrierClosure( + ordered_cache_names_.size(), + base::Bind(&SizeRetrievedFromAllCaches, + base::Passed(std::move(accumulator)), callback)); + + for (const std::string& cache_name : ordered_cache_names_) { + scoped_refptr cache = GetLoadedCache(cache_name); + cache->Size(base::Bind(&SizeRetrievedFromCache, cache, barrier_closure, + accumulator_ptr)); + } } void CacheStorage::PendingClosure(const base::Closure& callback) { @@ -943,11 +995,11 @@ void CacheStorage::PendingBoolAndErrorCallback( void CacheStorage::PendingCacheAndErrorCallback( const CacheAndErrorCallback& callback, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error) { base::WeakPtr cache_storage = weak_factory_.GetWeakPtr(); - callback.Run(cache, error); + callback.Run(std::move(cache), error); if (cache_storage) scheduler_->CompleteOperationAndRunNext(); } @@ -975,4 +1027,13 @@ void CacheStorage::PendingResponseCallback( scheduler_->CompleteOperationAndRunNext(); } +void CacheStorage::PendingSizeCallback(const SizeCallback& callback, + int64_t size) { + base::WeakPtr cache_storage = weak_factory_.GetWeakPtr(); + + callback.Run(size); + if (cache_storage) + scheduler_->CompleteOperationAndRunNext(); +} + } // namespace content diff --git a/chromium/content/browser/cache_storage/cache_storage.h b/chromium/content/browser/cache_storage/cache_storage.h index b1bf67e75d6..e81dd2f1441 100644 --- a/chromium/content/browser/cache_storage/cache_storage.h +++ b/chromium/content/browser/cache_storage/cache_storage.h @@ -41,10 +41,12 @@ class CONTENT_EXPORT CacheStorage { public: typedef std::vector StringVector; typedef base::Callback BoolAndErrorCallback; - typedef base::Callback&, - CacheStorageError)> CacheAndErrorCallback; + typedef base::Callback, + CacheStorageError)> + CacheAndErrorCallback; typedef base::Callback StringsAndErrorCallback; + using SizeCallback = base::Callback; static const char kIndexFileName[]; @@ -52,8 +54,8 @@ class CONTENT_EXPORT CacheStorage { const base::FilePath& origin_path, bool memory_only, base::SequencedTaskRunner* cache_task_runner, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context, const GURL& origin); @@ -84,20 +86,19 @@ class CONTENT_EXPORT CacheStorage { const CacheStorageCache::ResponseCallback& callback); // Calls match on all of the caches in parallel, calling |callback| with the - // first response found. Note that if multiple caches have the same - // request/response then it is not defined which cache's response will be - // returned. If no response is found then |callback| is called with + // response from the first cache (in order of cache creation) to have the + // entry. If no response is found then |callback| is called with // CACHE_STORAGE_ERROR_NOT_FOUND. void MatchAllCaches(scoped_ptr request, const CacheStorageCache::ResponseCallback& callback); - // Calls close on each cache and runs the callback after all of them have - // closed. - void CloseAllCaches(const base::Closure& callback); + // Sums the sizes of each cache and closes them. Runs |callback| with the + // size. + void GetSizeThenCloseAllCaches(const SizeCallback& callback); - // The size of all of the origin's contents in memory. Returns 0 if the cache - // backend is not a memory backend. Runs synchronously. - int64_t MemoryBackedSize() const; + // The size of all of the origin's contents. This value should be used as an + // estimate only since the cache may be modified at any time. + void Size(const SizeCallback& callback); // The functions below are for tests to verify that the operations run // serially. @@ -106,10 +107,10 @@ class CONTENT_EXPORT CacheStorage { private: friend class TestCacheStorage; - + class CacheLoader; class MemoryLoader; class SimpleCacheLoader; - class CacheLoader; + struct CacheMatchResponse; typedef std::map> CacheMap; @@ -119,7 +120,7 @@ class CONTENT_EXPORT CacheStorage { const std::string& cache_name); // Holds a reference to a cache for thirty seconds. - void TemporarilyPreserveCache(const scoped_refptr& cache); + void TemporarilyPreserveCache(scoped_refptr cache); virtual void SchedulePreservedCacheRemoval( const base::Closure& callback); // Virtual for testing. void RemovePreservedCache(const CacheStorageCache* cache); @@ -135,9 +136,9 @@ class CONTENT_EXPORT CacheStorage { const CacheAndErrorCallback& callback); void CreateCacheDidCreateCache(const std::string& cache_name, const CacheAndErrorCallback& callback, - const scoped_refptr& cache); + scoped_refptr cache); void CreateCacheDidWriteIndex(const CacheAndErrorCallback& callback, - const scoped_refptr& cache, + scoped_refptr cache, bool success); // The HasCache callbacks are below. @@ -151,9 +152,11 @@ class CONTENT_EXPORT CacheStorage { void DeleteCacheDidClose(const std::string& cache_name, const BoolAndErrorCallback& callback, const StringVector& ordered_cache_names, - const scoped_refptr& cache); + scoped_refptr cache, + int64_t cache_size); void DeleteCacheDidWriteIndex(const std::string& cache_name, const BoolAndErrorCallback& callback, + int cache_size, bool success); void DeleteCacheDidCleanUp(const BoolAndErrorCallback& callback, bool success); @@ -165,7 +168,7 @@ class CONTENT_EXPORT CacheStorage { void MatchCacheImpl(const std::string& cache_name, scoped_ptr request, const CacheStorageCache::ResponseCallback& callback); - void MatchCacheDidMatch(const scoped_refptr& cache, + void MatchCacheDidMatch(scoped_refptr cache, const CacheStorageCache::ResponseCallback& callback, CacheStorageError error, scoped_ptr response, @@ -174,26 +177,28 @@ class CONTENT_EXPORT CacheStorage { // The MatchAllCaches callbacks are below. void MatchAllCachesImpl(scoped_ptr request, const CacheStorageCache::ResponseCallback& callback); - void MatchAllCachesDidMatch(scoped_refptr cache, - const base::Closure& barrier_closure, - CacheStorageCache::ResponseCallback* callback, - CacheStorageError error, - scoped_ptr response, - scoped_ptr handle); + void MatchAllCachesDidMatch( + scoped_refptr cache, + CacheMatchResponse* out_match_response, + const base::Closure& barrier_closure, + CacheStorageError error, + scoped_ptr service_worker_response, + scoped_ptr handle); void MatchAllCachesDidMatchAll( - scoped_ptr callback); + scoped_ptr> match_responses, + const CacheStorageCache::ResponseCallback& callback); - // The CloseAllCaches callbacks are below. - void CloseAllCachesImpl(const base::Closure& callback); + void GetSizeThenCloseAllCachesImpl(const SizeCallback& callback); + + void SizeImpl(const SizeCallback& callback); void PendingClosure(const base::Closure& callback); void PendingBoolAndErrorCallback(const BoolAndErrorCallback& callback, bool found, CacheStorageError error); - void PendingCacheAndErrorCallback( - const CacheAndErrorCallback& callback, - const scoped_refptr& cache, - CacheStorageError error); + void PendingCacheAndErrorCallback(const CacheAndErrorCallback& callback, + scoped_refptr cache, + CacheStorageError error); void PendingStringsAndErrorCallback(const StringsAndErrorCallback& callback, const StringVector& strings, CacheStorageError error); @@ -203,6 +208,8 @@ class CONTENT_EXPORT CacheStorage { scoped_ptr response, scoped_ptr blob_data_handle); + void PendingSizeCallback(const SizeCallback& callback, int64_t size); + // Whether or not we've loaded the list of cache names into memory. bool initialized_; bool initializing_; @@ -233,6 +240,12 @@ class CONTENT_EXPORT CacheStorage { std::map> preserved_caches_; + // The quota manager. + scoped_refptr quota_manager_proxy_; + + // The origin that this CacheStorage is associated with. + GURL origin_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(CacheStorage); diff --git a/chromium/content/browser/cache_storage/cache_storage.proto b/chromium/content/browser/cache_storage/cache_storage.proto index 807c1e548c4..07a61700cff 100644 --- a/chromium/content/browser/cache_storage/cache_storage.proto +++ b/chromium/content/browser/cache_storage/cache_storage.proto @@ -42,6 +42,7 @@ message CacheResponse { required ResponseType response_type = 3; repeated CacheHeaderMap headers = 4; optional string url = 5; + optional int64 response_time = 6; } message CacheMetadata { diff --git a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc index 08dffc32323..bc58008ae29 100644 --- a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc +++ b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc @@ -32,13 +32,14 @@ CacheStorageBlobToDiskCache::~CacheStorageBlobToDiskCache() { void CacheStorageBlobToDiskCache::StreamBlobToCache( disk_cache::ScopedEntryPtr entry, int disk_cache_body_index, - const scoped_refptr& request_context_getter, + net::URLRequestContextGetter* request_context_getter, scoped_ptr blob_data_handle, const EntryAndBoolCallback& callback) { DCHECK(entry); DCHECK_LE(0, disk_cache_body_index); DCHECK(blob_data_handle); DCHECK(!blob_request_); + DCHECK(request_context_getter); if (!request_context_getter->GetURLRequestContext()) { callback.Run(std::move(entry), false /* success */); diff --git a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.h b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.h index 18e6fd9730f..3f73f255a6b 100644 --- a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.h +++ b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache.h @@ -43,12 +43,11 @@ class CONTENT_EXPORT CacheStorageBlobToDiskCache // Writes the body of |blob_data_handle| to |entry| with index // |disk_cache_body_index|. |entry| is passed to the callback once complete. // Only call this once per instantiation of CacheStorageBlobToDiskCache. - void StreamBlobToCache( - disk_cache::ScopedEntryPtr entry, - int disk_cache_body_index, - const scoped_refptr& request_context_getter, - scoped_ptr blob_data_handle, - const EntryAndBoolCallback& callback); + void StreamBlobToCache(disk_cache::ScopedEntryPtr entry, + int disk_cache_body_index, + net::URLRequestContextGetter* request_context_getter, + scoped_ptr blob_data_handle, + const EntryAndBoolCallback& callback); // net::URLRequest::Delegate overrides for reading blobs. void OnResponseStarted(net::URLRequest* request) override; @@ -80,7 +79,10 @@ class CONTENT_EXPORT CacheStorageBlobToDiskCache int cache_entry_offset_; disk_cache::ScopedEntryPtr entry_; - scoped_refptr request_context_getter_; + + // Owned by CacheStorageCache which owns this. + net::URLRequestContextGetter* request_context_getter_; + int disk_cache_body_index_; scoped_ptr blob_request_; EntryAndBoolCallback callback_; diff --git a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc index 28e6c0c18b4..5734e0d8a31 100644 --- a/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc @@ -172,7 +172,7 @@ class CacheStorageBlobToDiskCacheTest : public testing::Test { cache_storage_blob_to_disk_cache_->StreamBlobToCache( std::move(disk_cache_entry_), kCacheEntryIndex, - url_request_context_getter_, std::move(new_data_handle), + url_request_context_getter_.get(), std::move(new_data_handle), base::Bind(&CacheStorageBlobToDiskCacheTest::StreamCallback, base::Unretained(this))); diff --git a/chromium/content/browser/cache_storage/cache_storage_cache.cc b/chromium/content/browser/cache_storage/cache_storage_cache.cc index 2fc74110e7e..cbe1889d4e6 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache.cc +++ b/chromium/content/browser/cache_storage/cache_storage_cache.cc @@ -15,6 +15,7 @@ #include "base/metrics/histogram_macros.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/thread_task_runner_handle.h" #include "content/browser/cache_storage/cache_storage.pb.h" #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" @@ -41,7 +42,7 @@ namespace { class CacheStorageCacheDataHandle : public storage::BlobDataBuilder::DataHandle { public: - CacheStorageCacheDataHandle(const scoped_refptr& cache, + CacheStorageCacheDataHandle(scoped_refptr cache, disk_cache::ScopedEntryPtr entry) : cache_(cache), entry_(std::move(entry)) {} @@ -58,13 +59,9 @@ typedef base::Callback)> MetadataCallback; enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY }; -// The maximum size of an individual cache. Ultimately cache size is controlled -// per-origin. -const int kMaxCacheBytes = 512 * 1024 * 1024; - -void NotReachedCompletionCallback(int rv) { - NOTREACHED(); -} +// The maximum size of each cache. Ultimately, cache size +// is controlled per-origin by the QuotaManager. +const int kMaxCacheBytes = std::numeric_limits::max(); blink::WebServiceWorkerResponseType ProtoResponseTypeToWebResponseType( CacheResponse::ResponseType response_type) { @@ -109,11 +106,10 @@ CacheResponse::ResponseType WebResponseTypeToProtoResponseType( // Copy headers out of a cache entry and into a protobuf. The callback is // guaranteed to be run. void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback); -void ReadMetadataDidReadMetadata( - disk_cache::Entry* entry, - const MetadataCallback& callback, - const scoped_refptr& buffer, - int rv); +void ReadMetadataDidReadMetadata(disk_cache::Entry* entry, + const MetadataCallback& callback, + scoped_refptr buffer, + int rv); bool VaryMatches(const ServiceWorkerHeaderMap& request, const ServiceWorkerHeaderMap& cached_request, @@ -147,6 +143,12 @@ bool VaryMatches(const ServiceWorkerHeaderMap& request, return true; } +GURL RemoveQueryParam(const GURL& url) { + url::Replacements replacements; + replacements.ClearQuery(); + return url.ReplaceComponents(replacements); +} + void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) { DCHECK(entry); @@ -163,11 +165,10 @@ void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) { read_header_callback.Run(read_rv); } -void ReadMetadataDidReadMetadata( - disk_cache::Entry* entry, - const MetadataCallback& callback, - const scoped_refptr& buffer, - int rv) { +void ReadMetadataDidReadMetadata(disk_cache::Entry* entry, + const MetadataCallback& callback, + scoped_refptr buffer, + int rv) { if (rv != buffer->size()) { callback.Run(scoped_ptr()); return; @@ -210,12 +211,20 @@ struct CacheStorageCache::OpenAllEntriesContext { // The state needed to pass between CacheStorageCache::MatchAll callbacks. struct CacheStorageCache::MatchAllContext { - explicit MatchAllContext(const CacheStorageCache::ResponsesCallback& callback) - : original_callback(callback), + MatchAllContext(scoped_ptr request, + const CacheStorageCacheQueryParams& match_params, + const ResponsesCallback& callback) + : request(std::move(request)), + options(match_params), + original_callback(callback), out_responses(new Responses), out_blob_data_handles(new BlobDataHandles) {} ~MatchAllContext() {} + scoped_ptr request; + + CacheStorageCacheQueryParams options; + // The callback passed to the MatchAll() function. ResponsesCallback original_callback; @@ -251,31 +260,22 @@ struct CacheStorageCache::KeysContext { // The state needed to pass between CacheStorageCache::Put callbacks. struct CacheStorageCache::PutContext { - PutContext( - const GURL& origin, - scoped_ptr request, - scoped_ptr response, - scoped_ptr blob_data_handle, - const CacheStorageCache::ErrorCallback& callback, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy) - : origin(origin), - request(std::move(request)), + PutContext(scoped_ptr request, + scoped_ptr response, + scoped_ptr blob_data_handle, + const CacheStorageCache::ErrorCallback& callback) + : request(std::move(request)), response(std::move(response)), blob_data_handle(std::move(blob_data_handle)), - callback(callback), - request_context_getter(request_context_getter), - quota_manager_proxy(quota_manager_proxy) {} + callback(callback) {} // Input parameters to the Put function. - GURL origin; scoped_ptr request; scoped_ptr response; scoped_ptr blob_data_handle; CacheStorageCache::ErrorCallback callback; - scoped_refptr request_context_getter; - scoped_refptr quota_manager_proxy; disk_cache::ScopedEntryPtr cache_entry; + int64_t available_bytes = 0; private: DISALLOW_COPY_AND_ASSIGN(PutContext); @@ -284,26 +284,30 @@ struct CacheStorageCache::PutContext { // static scoped_refptr CacheStorageCache::CreateMemoryCache( const GURL& origin, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + const std::string& cache_name, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context) { - return make_scoped_refptr( - new CacheStorageCache(origin, base::FilePath(), request_context_getter, - quota_manager_proxy, blob_context)); + return make_scoped_refptr(new CacheStorageCache( + origin, cache_name, base::FilePath(), std::move(request_context_getter), + std::move(quota_manager_proxy), blob_context)); } // static scoped_refptr CacheStorageCache::CreatePersistentCache( const GURL& origin, + const std::string& cache_name, const base::FilePath& path, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context) { return make_scoped_refptr(new CacheStorageCache( - origin, path, request_context_getter, quota_manager_proxy, blob_context)); + origin, cache_name, path, std::move(request_context_getter), + std::move(quota_manager_proxy), blob_context)); } CacheStorageCache::~CacheStorageCache() { + quota_manager_proxy_->NotifyOriginNoLongerInUse(origin_); } base::WeakPtr CacheStorageCache::AsWeakPtr() { @@ -327,7 +331,10 @@ void CacheStorageCache::Match(scoped_ptr request, base::Passed(std::move(request)), pending_callback)); } -void CacheStorageCache::MatchAll(const ResponsesCallback& callback) { +void CacheStorageCache::MatchAll( + scoped_ptr request, + const CacheStorageCacheQueryParams& match_params, + const ResponsesCallback& callback) { if (!LazyInitialize()) { callback.Run(CACHE_STORAGE_ERROR_STORAGE, scoped_ptr(), scoped_ptr()); @@ -337,9 +344,12 @@ void CacheStorageCache::MatchAll(const ResponsesCallback& callback) { ResponsesCallback pending_callback = base::Bind(&CacheStorageCache::PendingResponsesCallback, weak_ptr_factory_.GetWeakPtr(), callback); + + scoped_ptr context( + new MatchAllContext(std::move(request), match_params, pending_callback)); scheduler_->ScheduleOperation(base::Bind(&CacheStorageCache::MatchAllImpl, weak_ptr_factory_.GetWeakPtr(), - pending_callback)); + base::Passed(std::move(context)))); } void CacheStorageCache::BatchOperation( @@ -427,50 +437,63 @@ void CacheStorageCache::Close(const base::Closure& callback) { pending_callback)); } -int64_t CacheStorageCache::MemoryBackedSize() const { - if (backend_state_ != BACKEND_OPEN || !memory_only_) - return 0; +void CacheStorageCache::Size(const SizeCallback& callback) { + if (!LazyInitialize()) { + // TODO(jkarlin): Delete caches that can't be initialized. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, 0)); + return; + } - scoped_ptr backend_iter = - backend_->CreateIterator(); - disk_cache::Entry* entry = nullptr; + if (initializing_) { + // Note that Size doesn't use the scheduler, see header comments for why. + pending_size_callbacks_.push_back(callback); + return; + } - int64_t sum = 0; + // Run immediately so that we don't deadlock on + // CacheStorageCache::PutDidDelete's call to + // quota_manager_proxy_->GetUsageAndQuota. + SizeImpl(callback); +} - std::vector entries; - int rv = net::OK; - while ((rv = backend_iter->OpenNextEntry( - &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) { - entries.push_back(entry); // Open the entries without mutating them. +void CacheStorageCache::GetSizeThenClose(const SizeCallback& callback) { + if (!LazyInitialize()) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, 0)); + return; } - DCHECK_NE(net::ERR_IO_PENDING, rv) - << "Memory cache operations should be synchronous."; - for (disk_cache::Entry* entry : entries) { - sum += entry->GetDataSize(INDEX_HEADERS) + - entry->GetDataSize(INDEX_RESPONSE_BODY); - entry->Close(); - } + SizeCallback pending_callback = + base::Bind(&CacheStorageCache::PendingSizeCallback, + weak_ptr_factory_.GetWeakPtr(), callback); - return sum; + scheduler_->ScheduleOperation( + base::Bind(&CacheStorageCache::SizeImpl, weak_ptr_factory_.GetWeakPtr(), + base::Bind(&CacheStorageCache::GetSizeThenCloseDidGetSize, + weak_ptr_factory_.GetWeakPtr(), pending_callback))); } CacheStorageCache::CacheStorageCache( const GURL& origin, + const std::string& cache_name, const base::FilePath& path, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context) : origin_(origin), + cache_name_(cache_name), path_(path), - request_context_getter_(request_context_getter), - quota_manager_proxy_(quota_manager_proxy), + request_context_getter_(std::move(request_context_getter)), + quota_manager_proxy_(std::move(quota_manager_proxy)), blob_storage_context_(blob_context), - backend_state_(BACKEND_UNINITIALIZED), scheduler_(new CacheStorageScheduler()), - initializing_(false), memory_only_(path.empty()), weak_ptr_factory_(this) { + DCHECK(!origin_.is_empty()); + DCHECK(quota_manager_proxy_.get()); + + quota_manager_proxy_->NotifyOriginInUse(origin_); } bool CacheStorageCache::LazyInitialize() { @@ -638,28 +661,30 @@ void CacheStorageCache::MatchDidReadMetadata( std::move(blob_data_handle)); } -void CacheStorageCache::MatchAllImpl(const ResponsesCallback& callback) { +void CacheStorageCache::MatchAllImpl(scoped_ptr context) { DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); if (backend_state_ != BACKEND_OPEN) { - callback.Run(CACHE_STORAGE_ERROR_STORAGE, scoped_ptr(), - scoped_ptr()); + context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE, + scoped_ptr(), + scoped_ptr()); return; } OpenAllEntries(base::Bind(&CacheStorageCache::MatchAllDidOpenAllEntries, - weak_ptr_factory_.GetWeakPtr(), callback)); + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(context)))); } void CacheStorageCache::MatchAllDidOpenAllEntries( - const ResponsesCallback& callback, + scoped_ptr context, scoped_ptr entries_context, CacheStorageError error) { if (error != CACHE_STORAGE_OK) { - callback.Run(error, scoped_ptr(), scoped_ptr()); + context->original_callback.Run(error, scoped_ptr(), + scoped_ptr()); return; } - scoped_ptr context(new MatchAllContext(callback)); context->entries_context.swap(entries_context); Entries::iterator iter = context->entries_context->entries.begin(); MatchAllProcessNextEntry(std::move(context), iter); @@ -676,6 +701,17 @@ void CacheStorageCache::MatchAllProcessNextEntry( return; } + if (context->options.ignore_search) { + DCHECK(context->request); + disk_cache::Entry* entry(*iter); + if (RemoveQueryParam(context->request->url) != + RemoveQueryParam(GURL(entry->GetKey()))) { + // In this case, we don't need to read data. + MatchAllProcessNextEntry(std::move(context), iter + 1); + return; + } + } + ReadMetadata(*iter, base::Bind(&CacheStorageCache::MatchAllDidReadMetadata, weak_ptr_factory_.GetWeakPtr(), base::Passed(std::move(context)), iter)); @@ -740,7 +776,9 @@ void CacheStorageCache::Put(const CacheStorageBatchOperation& operation, operation.response.status_text, operation.response.response_type, operation.response.headers, operation.response.blob_uuid, operation.response.blob_size, operation.response.stream_url, - operation.response.error)); + operation.response.error, operation.response.response_time, + false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */)); scoped_ptr blob_data_handle; @@ -762,9 +800,8 @@ void CacheStorageCache::Put(const CacheStorageBatchOperation& operation, weak_ptr_factory_.GetWeakPtr(), callback); scoped_ptr put_context( - new PutContext(origin_, std::move(request), std::move(response), - std::move(blob_data_handle), pending_callback, - request_context_getter_, quota_manager_proxy_)); + new PutContext(std::move(request), std::move(response), + std::move(blob_data_handle), pending_callback)); scheduler_->ScheduleOperation( base::Bind(&CacheStorageCache::PutImpl, weak_ptr_factory_.GetWeakPtr(), @@ -781,7 +818,7 @@ void CacheStorageCache::PutImpl(scoped_ptr put_context) { scoped_ptr request_copy( new ServiceWorkerFetchRequest(*put_context->request)); - DeleteImpl(std::move(request_copy), + DeleteImpl(std::move(request_copy), CacheStorageCacheQueryParams(), base::Bind(&CacheStorageCache::PutDidDelete, weak_ptr_factory_.GetWeakPtr(), base::Passed(std::move(put_context)))); @@ -794,6 +831,31 @@ void CacheStorageCache::PutDidDelete(scoped_ptr put_context, return; } + quota_manager_proxy_->GetUsageAndQuota( + base::ThreadTaskRunnerHandle::Get().get(), origin_, + storage::kStorageTypeTemporary, + base::Bind(&CacheStorageCache::PutDidGetUsageAndQuota, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(put_context)))); +} + +void CacheStorageCache::PutDidGetUsageAndQuota( + scoped_ptr put_context, + storage::QuotaStatusCode status_code, + int64_t usage, + int64_t quota) { + if (backend_state_ != BACKEND_OPEN) { + put_context->callback.Run(CACHE_STORAGE_ERROR_STORAGE); + return; + } + + if (status_code != storage::kQuotaStatusOk) { + put_context->callback.Run(CACHE_STORAGE_ERROR_QUOTA_EXCEEDED); + return; + } + + put_context->available_bytes = quota - usage; + scoped_ptr scoped_entry_ptr(new disk_cache::Entry*()); disk_cache::Entry** entry_ptr = scoped_entry_ptr.get(); ServiceWorkerFetchRequest* request_ptr = put_context->request.get(); @@ -841,6 +903,8 @@ void CacheStorageCache::PutDidCreateEntry( response_metadata->set_response_type( WebResponseTypeToProtoResponseType(put_context->response->response_type)); response_metadata->set_url(put_context->response->url.spec()); + response_metadata->set_response_time( + put_context->response->response_time.ToInternalValue()); for (ServiceWorkerHeaderMap::const_iterator it = put_context->response->headers.begin(); it != put_context->response->headers.end(); ++it) { @@ -860,6 +924,12 @@ void CacheStorageCache::PutDidCreateEntry( scoped_refptr buffer( new net::StringIOBuffer(std::move(serialized))); + int64_t bytes_to_write = buffer->size() + put_context->response->blob_size; + if (put_context->available_bytes < bytes_to_write) { + put_context->callback.Run(CACHE_STORAGE_ERROR_QUOTA_EXCEEDED); + return; + } + // Get a temporary copy of the entry pointer before passing it in base::Bind. disk_cache::Entry* temp_entry_ptr = put_context->cache_entry.get(); @@ -888,13 +958,7 @@ void CacheStorageCache::PutDidWriteHeaders(scoped_ptr put_context, // from the blob into the cache entry. if (put_context->response->blob_uuid.empty()) { - if (put_context->quota_manager_proxy.get()) { - put_context->quota_manager_proxy->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, put_context->origin, - storage::kStorageTypeTemporary, - put_context->cache_entry->GetDataSize(INDEX_HEADERS)); - } - + UpdateCacheSize(); put_context->callback.Run(CACHE_STORAGE_OK); return; } @@ -909,14 +973,11 @@ void CacheStorageCache::PutDidWriteHeaders(scoped_ptr put_context, BlobToDiskCacheIDMap::KeyType blob_to_cache_key = active_blob_to_disk_cache_writers_.Add(blob_to_cache); - // Grab some pointers before passing put_context in Bind. - scoped_refptr request_context_getter = - put_context->request_context_getter; scoped_ptr blob_data_handle = std::move(put_context->blob_data_handle); blob_to_cache->StreamBlobToCache( - std::move(entry), INDEX_RESPONSE_BODY, request_context_getter, + std::move(entry), INDEX_RESPONSE_BODY, request_context_getter_.get(), std::move(blob_data_handle), base::Bind(&CacheStorageCache::PutDidWriteBlobToCache, weak_ptr_factory_.GetWeakPtr(), @@ -939,17 +1000,32 @@ void CacheStorageCache::PutDidWriteBlobToCache( return; } - if (put_context->quota_manager_proxy.get()) { - put_context->quota_manager_proxy->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, put_context->origin, - storage::kStorageTypeTemporary, - put_context->cache_entry->GetDataSize(INDEX_HEADERS) + - put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY)); - } - + UpdateCacheSize(); put_context->callback.Run(CACHE_STORAGE_OK); } +void CacheStorageCache::UpdateCacheSize() { + if (backend_state_ != BACKEND_OPEN) + return; + + // Note that the callback holds a refptr to |this| since UpdateCacheSize is + // often called after an operation completes and the cache might be freed. + int rv = backend_->CalculateSizeOfAllEntries( + base::Bind(&CacheStorageCache::UpdateCacheSizeGotSize, this)); + + if (rv != net::ERR_IO_PENDING) + UpdateCacheSizeGotSize(rv); +} + +void CacheStorageCache::UpdateCacheSizeGotSize(int current_cache_size) { + int64_t old_cache_size = cache_size_; + cache_size_ = current_cache_size; + + quota_manager_proxy_->NotifyStorageModified( + storage::QuotaClient::kServiceWorkerCache, origin_, + storage::kStorageTypeTemporary, current_cache_size - old_cache_size); +} + void CacheStorageCache::Delete(const CacheStorageBatchOperation& operation, const ErrorCallback& callback) { DCHECK(BACKEND_OPEN == backend_state_ || initializing_); @@ -966,17 +1042,27 @@ void CacheStorageCache::Delete(const CacheStorageBatchOperation& operation, weak_ptr_factory_.GetWeakPtr(), callback); scheduler_->ScheduleOperation( base::Bind(&CacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(), - base::Passed(std::move(request)), pending_callback)); + base::Passed(std::move(request)), operation.match_params, + pending_callback)); } void CacheStorageCache::DeleteImpl( scoped_ptr request, + const CacheStorageCacheQueryParams& match_params, const ErrorCallback& callback) { DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); if (backend_state_ != BACKEND_OPEN) { callback.Run(CACHE_STORAGE_ERROR_STORAGE); return; } + + if (match_params.ignore_search) { + OpenAllEntries(base::Bind(&CacheStorageCache::DeleteDidOpenAllEntries, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(request)), callback)); + return; + } + scoped_ptr entry(new disk_cache::Entry*); disk_cache::Entry** entry_ptr = entry.get(); @@ -986,7 +1072,7 @@ void CacheStorageCache::DeleteImpl( net::CompletionCallback open_entry_callback = base::Bind( &CacheStorageCache::DeleteDidOpenEntry, weak_ptr_factory_.GetWeakPtr(), origin_, base::Passed(std::move(request)), callback, - base::Passed(std::move(entry)), quota_manager_proxy_); + base::Passed(std::move(entry))); int rv = backend_->OpenEntry(request_ptr->url.spec(), entry_ptr, open_entry_callback); @@ -994,12 +1080,35 @@ void CacheStorageCache::DeleteImpl( open_entry_callback.Run(rv); } +void CacheStorageCache::DeleteDidOpenAllEntries( + scoped_ptr request, + const ErrorCallback& callback, + scoped_ptr entries_context, + CacheStorageError error) { + if (error != CACHE_STORAGE_OK) { + callback.Run(error); + return; + } + + GURL request_url_without_query = RemoveQueryParam(request->url); + for (Entries::iterator iter = entries_context->entries.begin(); + iter != entries_context->entries.end(); iter++) { + disk_cache::Entry* entry(*iter); + if (request_url_without_query == RemoveQueryParam(GURL(entry->GetKey()))) + entry->Doom(); + } + + entries_context.reset(); + + UpdateCacheSize(); + callback.Run(CACHE_STORAGE_OK); +} + void CacheStorageCache::DeleteDidOpenEntry( const GURL& origin, scoped_ptr request, const CacheStorageCache::ErrorCallback& callback, scoped_ptr entry_ptr, - const scoped_refptr& quota_manager_proxy, int rv) { if (rv != net::OK) { callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND); @@ -1009,15 +1118,10 @@ void CacheStorageCache::DeleteDidOpenEntry( DCHECK(entry_ptr); disk_cache::ScopedEntryPtr entry(*entry_ptr); - if (quota_manager_proxy.get()) { - quota_manager_proxy->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, origin, - storage::kStorageTypeTemporary, - -1 * (entry->GetDataSize(INDEX_HEADERS) + - entry->GetDataSize(INDEX_RESPONSE_BODY))); - } - entry->Doom(); + entry.reset(); + + UpdateCacheSize(); callback.Run(CACHE_STORAGE_OK); } @@ -1107,6 +1211,19 @@ void CacheStorageCache::CloseImpl(const base::Closure& callback) { callback.Run(); } +void CacheStorageCache::SizeImpl(const SizeCallback& callback) { + DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); + + int64_t size = backend_state_ == BACKEND_OPEN ? cache_size_ : 0; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, size)); +} + +void CacheStorageCache::GetSizeThenCloseDidGetSize(const SizeCallback& callback, + int64_t cache_size) { + CloseImpl(base::Bind(callback, cache_size)); +} + void CacheStorageCache::CreateBackend(const ErrorCallback& callback) { DCHECK(!backend_); @@ -1158,19 +1275,40 @@ void CacheStorageCache::InitBackend() { scheduler_->ScheduleOperation(base::Bind( &CacheStorageCache::CreateBackend, weak_ptr_factory_.GetWeakPtr(), - base::Bind(&CacheStorageCache::InitDone, + base::Bind(&CacheStorageCache::InitDidCreateBackend, weak_ptr_factory_.GetWeakPtr()))); } -void CacheStorageCache::InitDone(CacheStorageError error) { +void CacheStorageCache::InitDidCreateBackend( + CacheStorageError cache_create_error) { + if (cache_create_error != CACHE_STORAGE_OK) { + InitGotCacheSize(cache_create_error, 0); + return; + } + + int rv = backend_->CalculateSizeOfAllEntries( + base::Bind(&CacheStorageCache::InitGotCacheSize, + weak_ptr_factory_.GetWeakPtr(), cache_create_error)); + + if (rv != net::ERR_IO_PENDING) + InitGotCacheSize(cache_create_error, rv); +} + +void CacheStorageCache::InitGotCacheSize(CacheStorageError cache_create_error, + int cache_size) { + cache_size_ = cache_size; initializing_ = false; - backend_state_ = (error == CACHE_STORAGE_OK && backend_ && + backend_state_ = (cache_create_error == CACHE_STORAGE_OK && backend_ && backend_state_ == BACKEND_UNINITIALIZED) ? BACKEND_OPEN : BACKEND_CLOSED; - UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", error, - CACHE_STORAGE_ERROR_LAST + 1); + for (const SizeCallback& callback : pending_size_callbacks_) + SizeImpl(callback); + pending_size_callbacks_.clear(); + + UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", + cache_create_error, CACHE_STORAGE_ERROR_LAST + 1); scheduler_->CompleteOperationAndRunNext(); } @@ -1227,6 +1365,15 @@ void CacheStorageCache::PendingRequestsCallback( scheduler_->CompleteOperationAndRunNext(); } +void CacheStorageCache::PendingSizeCallback(const SizeCallback& callback, + int64_t size) { + base::WeakPtr cache = weak_ptr_factory_.GetWeakPtr(); + + callback.Run(size); + if (cache) + scheduler_->CompleteOperationAndRunNext(); +} + void CacheStorageCache::PopulateResponseMetadata( const CacheMetadata& metadata, ServiceWorkerResponse* response) { @@ -1235,7 +1382,9 @@ void CacheStorageCache::PopulateResponseMetadata( metadata.response().status_text(), ProtoResponseTypeToWebResponseType(metadata.response().response_type()), ServiceWorkerHeaderMap(), "", 0, GURL(), - blink::WebServiceWorkerResponseErrorUnknown); + blink::WebServiceWorkerResponseErrorUnknown, + base::Time::FromInternalValue(metadata.response().response_time()), + true /* is_in_cache_storage */, cache_name_); for (int i = 0; i < metadata.response().headers_size(); ++i) { const CacheHeaderMap header = metadata.response().headers(i); diff --git a/chromium/content/browser/cache_storage/cache_storage_cache.h b/chromium/content/browser/cache_storage/cache_storage_cache.h index a9dcd4cccaf..4cb77ddb70a 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache.h +++ b/chromium/content/browser/cache_storage/cache_storage_cache.h @@ -18,6 +18,7 @@ #include "content/common/cache_storage/cache_storage_types.h" #include "content/common/service_worker/service_worker_types.h" #include "net/disk_cache/disk_cache.h" +#include "storage/common/quota/quota_status_code.h" namespace net { class URLRequestContextGetter; @@ -38,9 +39,9 @@ class CacheStorageScheduler; class TestCacheStorageCache; // Represents a ServiceWorker Cache as seen in -// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ -// The asynchronous methods are executed serially. Callbacks to the -// public functions will be called so long as the cache object lives. +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ The +// asynchronous methods are executed serially (except for Size). Callbacks to +// the public functions will be called so long as the cache object lives. class CONTENT_EXPORT CacheStorageCache : public base::RefCounted { public: @@ -57,26 +58,31 @@ class CONTENT_EXPORT CacheStorageCache using Requests = std::vector; using RequestsCallback = base::Callback)>; + using SizeCallback = base::Callback; static scoped_refptr CreateMemoryCache( const GURL& origin, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + const std::string& cache_name, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context); static scoped_refptr CreatePersistentCache( const GURL& origin, + const std::string& cache_name, const base::FilePath& path, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context); // Returns ERROR_TYPE_NOT_FOUND if not found. void Match(scoped_ptr request, const ResponseCallback& callback); - // Returns CACHE_STORAGE_OK and all responses in this cache. If there are no - // responses, returns CACHE_STORAGE_OK and an empty vector. - void MatchAll(const ResponsesCallback& callback); + // Returns CACHE_STORAGE_OK and matched responses in this cache. If there are + // no responses, returns CACHE_STORAGE_OK and an empty vector. + void MatchAll(scoped_ptr request, + const CacheStorageCacheQueryParams& match_params, + const ResponsesCallback& callback); // Runs given batch operations. This corresponds to the Batch Cache Operations // algorithm in the spec. @@ -106,9 +112,16 @@ class CONTENT_EXPORT CacheStorageCache // will exit early. Close should only be called once per CacheStorageCache. void Close(const base::Closure& callback); - // The size of the cache contents in memory. Returns 0 if the cache backend is - // not a memory cache backend. - int64_t MemoryBackedSize() const; + // The size of the cache's contents. This runs in parallel with other Cache + // operations. This is because QuotaManager is a dependency of the Put + // operation and QuotaManager calls Size. If the cache isn't yet initialized, + // runs immediately after initialization, before any pending operations in the + // scheduler are run. + void Size(const SizeCallback& callback); + + // Gets the cache's size, closes the backend, and then runs |callback| with + // the cache's size. + void GetSizeThenClose(const SizeCallback& callback); base::FilePath path() const { return path_; } @@ -141,9 +154,10 @@ class CONTENT_EXPORT CacheStorageCache CacheStorageCache( const GURL& origin, + const std::string& cache_name, const base::FilePath& path, - const scoped_refptr& request_context_getter, - const scoped_refptr& quota_manager_proxy, + scoped_refptr request_context_getter, + scoped_refptr quota_manager_proxy, base::WeakPtr blob_context); // Async operations in progress will cancel and not run their callbacks. @@ -171,9 +185,9 @@ class CONTENT_EXPORT CacheStorageCache scoped_ptr headers); // MatchAll callbacks - void MatchAllImpl(const ResponsesCallback& callback); + void MatchAllImpl(scoped_ptr context); void MatchAllDidOpenAllEntries( - const ResponsesCallback& callback, + scoped_ptr context, scoped_ptr entries_context, CacheStorageError error); void MatchAllProcessNextEntry(scoped_ptr context, @@ -190,6 +204,10 @@ class CONTENT_EXPORT CacheStorageCache void PutImpl(scoped_ptr put_context); void PutDidDelete(scoped_ptr put_context, CacheStorageError delete_error); + void PutDidGetUsageAndQuota(scoped_ptr put_context, + storage::QuotaStatusCode status_code, + int64_t usage, + int64_t quota); void PutDidCreateEntry(scoped_ptr entry_ptr, scoped_ptr put_context, int rv); @@ -201,18 +219,28 @@ class CONTENT_EXPORT CacheStorageCache disk_cache::ScopedEntryPtr entry, bool success); + // Asynchronously calculates the current cache size, notifies the quota + // manager of any change from the last report, and sets cache_size_ to the new + // size. Runs |callback| once complete. + void UpdateCacheSize(); + void UpdateCacheSizeGotSize(int current_cache_size); + // Returns ERROR_NOT_FOUND if not found. Otherwise deletes and returns OK. void Delete(const CacheStorageBatchOperation& operation, const ErrorCallback& callback); void DeleteImpl(scoped_ptr request, + const CacheStorageCacheQueryParams& match_params, const ErrorCallback& callback); - void DeleteDidOpenEntry( - const GURL& origin, + void DeleteDidOpenAllEntries( scoped_ptr request, - const CacheStorageCache::ErrorCallback& callback, - scoped_ptr entryptr, - const scoped_refptr& quota_manager_proxy, - int rv); + const ErrorCallback& callback, + scoped_ptr entries_context, + CacheStorageError error); + void DeleteDidOpenEntry(const GURL& origin, + scoped_ptr request, + const CacheStorageCache::ErrorCallback& callback, + scoped_ptr entryptr, + int rv); // Keys callbacks. void KeysImpl(const RequestsCallback& callback); @@ -227,6 +255,11 @@ class CONTENT_EXPORT CacheStorageCache void CloseImpl(const base::Closure& callback); + void SizeImpl(const SizeCallback& callback); + + void GetSizeThenCloseDidGetSize(const SizeCallback& callback, + int64_t cache_size); + // Loads the backend and calls the callback with the result (true for // success). The callback will always be called. Virtual for tests. virtual void CreateBackend(const ErrorCallback& callback); @@ -235,7 +268,8 @@ class CONTENT_EXPORT CacheStorageCache int rv); void InitBackend(); - void InitDone(CacheStorageError error); + void InitDidCreateBackend(CacheStorageError cache_create_error); + void InitGotCacheSize(CacheStorageError cache_create_error, int cache_size); void PendingClosure(const base::Closure& callback); void PendingErrorCallback(const ErrorCallback& callback, @@ -252,6 +286,7 @@ class CONTENT_EXPORT CacheStorageCache void PendingRequestsCallback(const RequestsCallback& callback, CacheStorageError error, scoped_ptr requests); + void PendingSizeCallback(const SizeCallback& callback, int64_t size); void PopulateResponseMetadata(const CacheMetadata& metadata, ServiceWorkerResponse* response); @@ -263,13 +298,16 @@ class CONTENT_EXPORT CacheStorageCache scoped_ptr backend_; GURL origin_; + const std::string cache_name_; base::FilePath path_; scoped_refptr request_context_getter_; scoped_refptr quota_manager_proxy_; base::WeakPtr blob_storage_context_; - BackendState backend_state_; + BackendState backend_state_ = BACKEND_UNINITIALIZED; scoped_ptr scheduler_; - bool initializing_; + std::vector pending_size_callbacks_; + bool initializing_ = false; + int64_t cache_size_ = 0; // Owns the elements of the list BlobToDiskCacheIDMap active_blob_to_disk_cache_writers_; diff --git a/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc index b440758990e..a8200769fb2 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc @@ -23,6 +23,7 @@ #include "content/common/service_worker/service_worker_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/referrer.h" +#include "content/public/test/mock_special_storage_policy.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/base/test_completion_callback.h" @@ -41,6 +42,8 @@ namespace content { namespace { const char kTestData[] = "Hello World"; +const char kOrigin[] = "http://example.com"; +const char kCacheName[] = "test_cache"; // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns // the memory. @@ -188,6 +191,14 @@ bool ResponseMetadataEqual(const ServiceWorkerResponse& expected, return false; } + EXPECT_EQ(expected.response_time, actual.response_time); + if (expected.response_time != actual.response_time) + return false; + + EXPECT_EQ(expected.cache_storage_cache_name, actual.cache_storage_cache_name); + if (expected.cache_storage_cache_name != actual.cache_storage_cache_name) + return false; + return true; } @@ -198,6 +209,14 @@ bool ResponseBodiesEqual(const std::string& expected_body, return expected_body == actual_body; } +ServiceWorkerResponse SetCacheName(const ServiceWorkerResponse& original) { + return ServiceWorkerResponse( + original.url, original.status_code, original.status_text, + original.response_type, original.headers, original.blob_uuid, + original.blob_size, original.stream_url, original.error, + original.response_time, true, kCacheName); +} + } // namespace // A CacheStorageCache that can optionally delay during backend creation. @@ -205,11 +224,13 @@ class TestCacheStorageCache : public CacheStorageCache { public: TestCacheStorageCache( const GURL& origin, + const std::string& cache_name, const base::FilePath& path, const scoped_refptr& request_context_getter, const scoped_refptr& quota_manager_proxy, base::WeakPtr blob_context) : CacheStorageCache(origin, + cache_name, path, request_context_getter, quota_manager_proxy, @@ -253,9 +274,7 @@ class TestCacheStorageCache : public CacheStorageCache { class CacheStorageCacheTest : public testing::Test { public: CacheStorageCacheTest() - : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), - callback_error_(CACHE_STORAGE_OK), - callback_closed_(false) {} + : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} void SetUp() override { ChromeBlobStorageContext* blob_storage_context = @@ -264,8 +283,19 @@ class CacheStorageCacheTest : public testing::Test { base::RunLoop().RunUntilIdle(); blob_storage_context_ = blob_storage_context->context(); + if (!MemoryOnly()) + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + quota_policy_ = new MockSpecialStoragePolicy; + mock_quota_manager_ = new MockQuotaManager( + MemoryOnly() /* is incognito */, temp_dir_.path(), + base::ThreadTaskRunnerHandle::Get().get(), + base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get()); + mock_quota_manager_->SetQuota(GURL(kOrigin), storage::kStorageTypeTemporary, + 1024 * 1024 * 100); + quota_manager_proxy_ = new MockQuotaManagerProxy( - nullptr, base::ThreadTaskRunnerHandle::Get().get()); + mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl); url_request_job_factory_->SetProtocolHandler( @@ -278,13 +308,10 @@ class CacheStorageCacheTest : public testing::Test { CreateRequests(blob_storage_context); - if (!MemoryOnly()) - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - base::FilePath path = MemoryOnly() ? base::FilePath() : temp_dir_.path(); - cache_ = make_scoped_refptr(new TestCacheStorageCache( - GURL("http://example.com"), path, browser_context_.GetRequestContext(), - quota_manager_proxy_, blob_storage_context->context()->AsWeakPtr())); + GURL(kOrigin), kCacheName, temp_dir_.path(), + browser_context_.GetRequestContext(), quota_manager_proxy_, + blob_storage_context->context()->AsWeakPtr())); } void TearDown() override { @@ -299,6 +326,9 @@ class CacheStorageCacheTest : public testing::Test { body_request_ = ServiceWorkerFetchRequest(GURL("http://example.com/body.html"), "GET", headers, Referrer(), false); + body_request_with_query_ = ServiceWorkerFetchRequest( + GURL("http://example.com/body.html?query=test"), "GET", headers, + Referrer(), false); no_body_request_ = ServiceWorkerFetchRequest(GURL("http://example.com/no_body.html"), "GET", headers, Referrer(), false); @@ -318,12 +348,24 @@ class CacheStorageCacheTest : public testing::Test { GURL("http://example.com/body.html"), 200, "OK", blink::WebServiceWorkerResponseTypeDefault, headers, blob_handle_->uuid(), expected_blob_data_.size(), GURL(), - blink::WebServiceWorkerResponseErrorUnknown); + blink::WebServiceWorkerResponseErrorUnknown, base::Time::Now(), + false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */); + + body_response_with_query_ = ServiceWorkerResponse( + GURL("http://example.com/body.html?query=test"), 200, "OK", + blink::WebServiceWorkerResponseTypeDefault, headers, + blob_handle_->uuid(), expected_blob_data_.size(), GURL(), + blink::WebServiceWorkerResponseErrorUnknown, base::Time::Now(), + false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */); no_body_response_ = ServiceWorkerResponse( GURL("http://example.com/no_body.html"), 200, "OK", blink::WebServiceWorkerResponseTypeDefault, headers, "", 0, GURL(), - blink::WebServiceWorkerResponseErrorUnknown); + blink::WebServiceWorkerResponseErrorUnknown, base::Time::Now(), + false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */); } scoped_ptr CopyFetchRequest( @@ -372,20 +414,33 @@ class CacheStorageCacheTest : public testing::Test { return callback_error_ == CACHE_STORAGE_OK; } - bool MatchAll(scoped_ptr* responses, + bool MatchAll(const ServiceWorkerFetchRequest& request, + const CacheStorageCacheQueryParams& match_params, + scoped_ptr* responses, scoped_ptr* body_handles) { base::RunLoop loop; - cache_->MatchAll(base::Bind( - &CacheStorageCacheTest::ResponsesAndErrorCallback, - base::Unretained(this), loop.QuitClosure(), responses, body_handles)); + cache_->MatchAll( + CopyFetchRequest(request), match_params, + base::Bind(&CacheStorageCacheTest::ResponsesAndErrorCallback, + base::Unretained(this), loop.QuitClosure(), responses, + body_handles)); loop.Run(); return callback_error_ == CACHE_STORAGE_OK; } - bool Delete(const ServiceWorkerFetchRequest& request) { + bool MatchAll(scoped_ptr* responses, + scoped_ptr* body_handles) { + return MatchAll(ServiceWorkerFetchRequest(), CacheStorageCacheQueryParams(), + responses, body_handles); + } + + bool Delete(const ServiceWorkerFetchRequest& request, + const CacheStorageCacheQueryParams& match_params = + CacheStorageCacheQueryParams()) { CacheStorageBatchOperation operation; operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE; operation.request = request; + operation.match_params = match_params; CacheStorageError error = BatchOperation(std::vector(1, operation)); @@ -413,6 +468,32 @@ class CacheStorageCacheTest : public testing::Test { return callback_closed_; } + int64_t Size() { + // Storage notification happens after an operation completes. Let the any + // notifications complete before calling Size. + base::RunLoop().RunUntilIdle(); + + base::RunLoop run_loop; + bool callback_called = false; + cache_->Size(base::Bind(&CacheStorageCacheTest::SizeCallback, + base::Unretained(this), &run_loop, + &callback_called)); + run_loop.Run(); + EXPECT_TRUE(callback_called); + return callback_size_; + } + + int64_t GetSizeThenClose() { + base::RunLoop run_loop; + bool callback_called = false; + cache_->GetSizeThenClose(base::Bind(&CacheStorageCacheTest::SizeCallback, + base::Unretained(this), &run_loop, + &callback_called)); + run_loop.Run(); + EXPECT_TRUE(callback_called); + return callback_size_; + } + void RequestsCallback(base::RunLoop* run_loop, CacheStorageError error, scoped_ptr requests) { @@ -477,6 +558,15 @@ class CacheStorageCacheTest : public testing::Test { run_loop->Quit(); } + void SizeCallback(base::RunLoop* run_loop, + bool* callback_called, + int64_t size) { + *callback_called = true; + callback_size_ = size; + if (run_loop) + run_loop->Quit(); + } + bool VerifyKeys(const std::vector& expected_keys) { if (expected_keys.size() != callback_strings_.size()) return false; @@ -514,6 +604,8 @@ class CacheStorageCacheTest : public testing::Test { TestBrowserContext browser_context_; TestBrowserThreadBundle browser_thread_bundle_; scoped_ptr url_request_job_factory_; + scoped_refptr quota_policy_; + scoped_refptr mock_quota_manager_; scoped_refptr quota_manager_proxy_; storage::BlobStorageContext* blob_storage_context_; @@ -521,16 +613,19 @@ class CacheStorageCacheTest : public testing::Test { ServiceWorkerFetchRequest body_request_; ServiceWorkerResponse body_response_; + ServiceWorkerFetchRequest body_request_with_query_; + ServiceWorkerResponse body_response_with_query_; ServiceWorkerFetchRequest no_body_request_; ServiceWorkerResponse no_body_response_; scoped_ptr blob_handle_; std::string expected_blob_data_; - CacheStorageError callback_error_; + CacheStorageError callback_error_ = CACHE_STORAGE_OK; scoped_ptr callback_response_; scoped_ptr callback_response_data_; std::vector callback_strings_; - bool callback_closed_; + bool callback_closed_ = false; + int64_t callback_size_ = 0; }; class CacheStorageCacheTestP : public CacheStorageCacheTest, @@ -538,12 +633,6 @@ class CacheStorageCacheTestP : public CacheStorageCacheTest, bool MemoryOnly() override { return !GetParam(); } }; -class CacheStorageCacheMemoryOnlyTest - : public CacheStorageCacheTest, - public testing::WithParamInterface { - bool MemoryOnly() override { return true; } -}; - TEST_P(CacheStorageCacheTestP, PutNoBody) { EXPECT_TRUE(Put(no_body_request_, no_body_response_)); } @@ -666,14 +755,16 @@ TEST_P(CacheStorageCacheTestP, PutReplcaceInBatch) { TEST_P(CacheStorageCacheTestP, MatchNoBody) { EXPECT_TRUE(Put(no_body_request_, no_body_response_)); EXPECT_TRUE(Match(no_body_request_)); - EXPECT_TRUE(ResponseMetadataEqual(no_body_response_, *callback_response_)); + EXPECT_TRUE(ResponseMetadataEqual(SetCacheName(no_body_response_), + *callback_response_)); EXPECT_FALSE(callback_response_data_); } TEST_P(CacheStorageCacheTestP, MatchBody) { EXPECT_TRUE(Put(body_request_, body_response_)); EXPECT_TRUE(Match(body_request_)); - EXPECT_TRUE(ResponseMetadataEqual(body_response_, *callback_response_)); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(body_response_), *callback_response_)); EXPECT_TRUE( ResponseBodiesEqual(expected_blob_data_, *callback_response_data_)); } @@ -694,7 +785,8 @@ TEST_P(CacheStorageCacheTestP, MatchAll_NoBody) { EXPECT_TRUE(MatchAll(&responses, &body_handles)); ASSERT_EQ(1u, responses->size()); - EXPECT_TRUE(ResponseMetadataEqual(no_body_response_, responses->at(0))); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(no_body_response_), responses->at(0))); EXPECT_TRUE(body_handles->empty()); } @@ -707,7 +799,8 @@ TEST_P(CacheStorageCacheTestP, MatchAll_Body) { ASSERT_EQ(1u, responses->size()); ASSERT_EQ(1u, body_handles->size()); - EXPECT_TRUE(ResponseMetadataEqual(body_response_, responses->at(0))); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(body_response_), responses->at(0))); EXPECT_TRUE(ResponseBodiesEqual(expected_blob_data_, body_handles->at(0))); } @@ -725,10 +818,12 @@ TEST_P(CacheStorageCacheTestP, MatchAll_TwoResponsesThenOne) { std::set matched_set; for (const ServiceWorkerResponse& response : *responses) { if (response.url.spec() == "http://example.com/no_body.html") { - EXPECT_TRUE(ResponseMetadataEqual(no_body_response_, response)); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(no_body_response_), response)); matched_set.insert(response.url.spec()); } else if (response.url.spec() == "http://example.com/body.html") { - EXPECT_TRUE(ResponseMetadataEqual(body_response_, response)); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(body_response_), response)); EXPECT_TRUE( ResponseBodiesEqual(expected_blob_data_, body_handles->at(0))); matched_set.insert(response.url.spec()); @@ -743,10 +838,41 @@ TEST_P(CacheStorageCacheTestP, MatchAll_TwoResponsesThenOne) { EXPECT_TRUE(MatchAll(&responses, &body_handles)); ASSERT_EQ(1u, responses->size()); - EXPECT_TRUE(ResponseMetadataEqual(no_body_response_, responses->at(0))); + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(no_body_response_), responses->at(0))); EXPECT_TRUE(body_handles->empty()); } +TEST_P(CacheStorageCacheTestP, MatchAll_IgnoreSearch) { + EXPECT_TRUE(Put(body_request_, body_response_)); + EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); + EXPECT_TRUE(Put(no_body_request_, no_body_response_)); + + scoped_ptr responses; + scoped_ptr body_handles; + CacheStorageCacheQueryParams match_params; + match_params.ignore_search = true; + EXPECT_TRUE(MatchAll(body_request_, match_params, &responses, &body_handles)); + + ASSERT_EQ(2u, responses->size()); + ASSERT_EQ(2u, body_handles->size()); + + // Order of returned responses is not guaranteed. + std::set matched_set; + for (const ServiceWorkerResponse& response : *responses) { + if (response.url.spec() == "http://example.com/body.html?query=test") { + EXPECT_TRUE(ResponseMetadataEqual(SetCacheName(body_response_with_query_), + response)); + matched_set.insert(response.url.spec()); + } else if (response.url.spec() == "http://example.com/body.html") { + EXPECT_TRUE( + ResponseMetadataEqual(SetCacheName(body_response_), response)); + matched_set.insert(response.url.spec()); + } + } + EXPECT_EQ(2u, matched_set.size()); +} + TEST_P(CacheStorageCacheTestP, Vary) { body_request_.headers["vary_foo"] = "foo"; body_response_.headers["vary"] = "vary_foo"; @@ -863,6 +989,61 @@ TEST_P(CacheStorageCacheTestP, DeleteBody) { EXPECT_TRUE(Delete(body_request_)); } +TEST_P(CacheStorageCacheTestP, DeleteWithIgnoreSearchTrue) { + EXPECT_TRUE(Put(no_body_request_, no_body_response_)); + EXPECT_TRUE(Put(body_request_, body_response_)); + EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); + + EXPECT_TRUE(Keys()); + EXPECT_EQ(3u, callback_strings_.size()); + std::vector expected_keys; + expected_keys.push_back(no_body_request_.url.spec()); + expected_keys.push_back(body_request_.url.spec()); + expected_keys.push_back(body_request_with_query_.url.spec()); + EXPECT_TRUE(VerifyKeys(expected_keys)); + + // The following delete operation will remove both of body_request_ and + // body_request_with_query_ from cache storage. + CacheStorageCacheQueryParams match_params; + match_params.ignore_search = true; + EXPECT_TRUE(Delete(body_request_with_query_, match_params)); + + EXPECT_TRUE(Keys()); + EXPECT_EQ(1u, callback_strings_.size()); + expected_keys.clear(); + expected_keys.push_back(no_body_request_.url.spec()); + EXPECT_TRUE(VerifyKeys(expected_keys)); +} + +TEST_P(CacheStorageCacheTestP, DeleteWithIgnoreSearchFalse) { + EXPECT_TRUE(Put(no_body_request_, no_body_response_)); + EXPECT_TRUE(Put(body_request_, body_response_)); + EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); + + EXPECT_TRUE(Keys()); + EXPECT_EQ(3u, callback_strings_.size()); + std::vector expected_keys; + expected_keys.push_back(no_body_request_.url.spec()); + expected_keys.push_back(body_request_.url.spec()); + expected_keys.push_back(body_request_with_query_.url.spec()); + EXPECT_TRUE(VerifyKeys(expected_keys)); + + // Default value of ignore_search is false. + CacheStorageCacheQueryParams match_params; + match_params.ignore_search = false; + EXPECT_EQ(match_params.ignore_search, + CacheStorageCacheQueryParams().ignore_search); + + EXPECT_TRUE(Delete(body_request_with_query_, match_params)); + + EXPECT_TRUE(Keys()); + EXPECT_EQ(2u, callback_strings_.size()); + expected_keys.clear(); + expected_keys.push_back(no_body_request_.url.spec()); + expected_keys.push_back(body_request_.url.spec()); + EXPECT_TRUE(VerifyKeys(expected_keys)); +} + TEST_P(CacheStorageCacheTestP, QuickStressNoBody) { for (int i = 0; i < 100; ++i) { EXPECT_FALSE(Match(no_body_request_)); @@ -895,7 +1076,9 @@ TEST_F(CacheStorageCacheTest, CaselessServiceWorkerResponseHeaders) { ServiceWorkerResponse response(GURL("http://www.example.com"), 200, "OK", blink::WebServiceWorkerResponseTypeDefault, ServiceWorkerHeaderMap(), "", 0, GURL(), - blink::WebServiceWorkerResponseErrorUnknown); + blink::WebServiceWorkerResponseErrorUnknown, + base::Time(), false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */); response.headers["content-type"] = "foo"; response.headers["Content-Type"] = "bar"; EXPECT_EQ("bar", response.headers["content-type"]); @@ -916,46 +1099,95 @@ TEST_P(CacheStorageCacheTestP, QuotaManagerModified) { EXPECT_EQ(0, quota_manager_proxy_->notify_storage_modified_count()); EXPECT_TRUE(Put(no_body_request_, no_body_response_)); + // Storage notification happens after the operation returns, so continue the + // event loop. + base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, quota_manager_proxy_->notify_storage_modified_count()); EXPECT_LT(0, quota_manager_proxy_->last_notified_delta()); int64_t sum_delta = quota_manager_proxy_->last_notified_delta(); EXPECT_TRUE(Put(body_request_, body_response_)); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(2, quota_manager_proxy_->notify_storage_modified_count()); EXPECT_LT(sum_delta, quota_manager_proxy_->last_notified_delta()); sum_delta += quota_manager_proxy_->last_notified_delta(); EXPECT_TRUE(Delete(body_request_)); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(3, quota_manager_proxy_->notify_storage_modified_count()); sum_delta += quota_manager_proxy_->last_notified_delta(); EXPECT_TRUE(Delete(no_body_request_)); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(4, quota_manager_proxy_->notify_storage_modified_count()); sum_delta += quota_manager_proxy_->last_notified_delta(); EXPECT_EQ(0, sum_delta); } -TEST_F(CacheStorageCacheMemoryOnlyTest, MemoryBackedSize) { - EXPECT_EQ(0, cache_->MemoryBackedSize()); +TEST_P(CacheStorageCacheTestP, PutObeysQuotaLimits) { + mock_quota_manager_->SetQuota(GURL(kOrigin), storage::kStorageTypeTemporary, + 0); + EXPECT_FALSE(Put(no_body_request_, no_body_response_)); + EXPECT_EQ(CACHE_STORAGE_ERROR_QUOTA_EXCEEDED, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, Size) { + EXPECT_EQ(0, Size()); EXPECT_TRUE(Put(no_body_request_, no_body_response_)); - EXPECT_LT(0, cache_->MemoryBackedSize()); - int64_t no_body_size = cache_->MemoryBackedSize(); + EXPECT_LT(0, Size()); + int64_t no_body_size = Size(); EXPECT_TRUE(Delete(no_body_request_)); - EXPECT_EQ(0, cache_->MemoryBackedSize()); + EXPECT_EQ(0, Size()); EXPECT_TRUE(Put(body_request_, body_response_)); - EXPECT_LT(no_body_size, cache_->MemoryBackedSize()); + EXPECT_LT(no_body_size, Size()); EXPECT_TRUE(Delete(body_request_)); - EXPECT_EQ(0, cache_->MemoryBackedSize()); + EXPECT_EQ(0, Size()); } -TEST_F(CacheStorageCacheTest, MemoryBackedSizePersistent) { - EXPECT_EQ(0, cache_->MemoryBackedSize()); - EXPECT_TRUE(Put(no_body_request_, no_body_response_)); - EXPECT_EQ(0, cache_->MemoryBackedSize()); +TEST_P(CacheStorageCacheTestP, SizeOperationsArePrioritized) { + // Test that pending size operations (those waiting for initialization) run + // before other scheduler operations. + cache_->set_delay_backend_creation(true); // Delay cache initialization + + CacheStorageBatchOperation operation; + operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; + operation.request = body_request_; + operation.response = body_response_; + + callback_error_ = CACHE_STORAGE_ERROR_NOT_FOUND; + base::RunLoop run_loop; + // Start a put operation that blocks on initialization. + cache_->BatchOperation(std::vector(1, operation), + base::Bind(&CacheStorageCacheTest::ErrorTypeCallback, + base::Unretained(this), &run_loop)); + + // Next start a size operation that also blocks on initialization. + bool size_callback_called = false; + cache_->Size(base::Bind(&CacheStorageCacheTest::SizeCallback, + base::Unretained(this), nullptr, + &size_callback_called)); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(size_callback_called); + EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); + + // Finish initialization. The Size operation should complete before Put gets + // to run as Size has priority. See crbug.com/605663. + cache_->ContinueCreateBackend(); + run_loop.Run(); + EXPECT_TRUE(size_callback_called); + EXPECT_EQ(CACHE_STORAGE_OK, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, GetSizeThenClose) { + EXPECT_TRUE(Put(body_request_, body_response_)); + int64_t cache_size = Size(); + EXPECT_EQ(cache_size, GetSizeThenClose()); + VerifyAllOpsFail(); } TEST_P(CacheStorageCacheTestP, OpsFailOnClosedBackendNeverCreated) { diff --git a/chromium/content/browser/cache_storage/cache_storage_context_impl.cc b/chromium/content/browser/cache_storage/cache_storage_context_impl.cc index 83aa12d6cdc..e58d790939c 100644 --- a/chromium/content/browser/cache_storage/cache_storage_context_impl.cc +++ b/chromium/content/browser/cache_storage/cache_storage_context_impl.cc @@ -28,8 +28,7 @@ CacheStorageContextImpl::~CacheStorageContextImpl() { void CacheStorageContextImpl::Init( const base::FilePath& user_data_directory, - storage::QuotaManagerProxy* quota_manager_proxy, - storage::SpecialStoragePolicy* special_storage_policy) { + scoped_refptr quota_manager_proxy) { DCHECK_CURRENTLY_ON(BrowserThread::UI); is_incognito_ = user_data_directory.empty(); @@ -45,7 +44,7 @@ void CacheStorageContextImpl::Init( // TODO: Fix the tests to let the quota manager initialize normally. if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { CreateCacheStorageManager(user_data_directory, cache_task_runner, - quota_manager_proxy, special_storage_policy); + std::move(quota_manager_proxy)); return; } @@ -53,8 +52,7 @@ void CacheStorageContextImpl::Init( BrowserThread::IO, FROM_HERE, base::Bind(&CacheStorageContextImpl::CreateCacheStorageManager, this, user_data_directory, cache_task_runner, - make_scoped_refptr(quota_manager_proxy), - make_scoped_refptr(special_storage_policy))); + std::move(quota_manager_proxy))); } void CacheStorageContextImpl::Shutdown() { @@ -103,15 +101,13 @@ void CacheStorageContextImpl::DeleteForOrigin(const GURL& origin) { void CacheStorageContextImpl::CreateCacheStorageManager( const base::FilePath& user_data_directory, - const scoped_refptr& cache_task_runner, - storage::QuotaManagerProxy* quota_manager_proxy, - storage::SpecialStoragePolicy* special_storage_policy) { + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!cache_manager_); - cache_manager_ = - CacheStorageManager::Create(user_data_directory, cache_task_runner.get(), - make_scoped_refptr(quota_manager_proxy)); + cache_manager_ = CacheStorageManager::Create( + user_data_directory, cache_task_runner, std::move(quota_manager_proxy)); } void CacheStorageContextImpl::ShutdownOnIO() { diff --git a/chromium/content/browser/cache_storage/cache_storage_context_impl.h b/chromium/content/browser/cache_storage/cache_storage_context_impl.h index d72babf1d37..f404fa86d31 100644 --- a/chromium/content/browser/cache_storage/cache_storage_context_impl.h +++ b/chromium/content/browser/cache_storage/cache_storage_context_impl.h @@ -44,8 +44,7 @@ class CONTENT_EXPORT CacheStorageContextImpl // Init and Shutdown are for use on the UI thread when the profile, // storagepartition is being setup and torn down. void Init(const base::FilePath& user_data_directory, - storage::QuotaManagerProxy* quota_manager_proxy, - storage::SpecialStoragePolicy* special_storage_policy); + scoped_refptr quota_manager_proxy); void Shutdown(); // Only callable on the IO thread. @@ -73,9 +72,8 @@ class CONTENT_EXPORT CacheStorageContextImpl private: void CreateCacheStorageManager( const base::FilePath& user_data_directory, - const scoped_refptr& cache_task_runner, - storage::QuotaManagerProxy* quota_manager_proxy, - storage::SpecialStoragePolicy* special_storage_policy); + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy); void ShutdownOnIO(); diff --git a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc index 073f57d8027..f371be943dc 100644 --- a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc +++ b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc @@ -22,6 +22,8 @@ #include "content/public/common/origin_util.h" #include "storage/browser/blob/blob_data_handle.h" #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerCacheError.h" +#include "url/gurl.h" +#include "url/origin.h" namespace content { @@ -43,13 +45,17 @@ blink::WebServiceWorkerCacheError ToWebServiceWorkerCacheError( return blink::WebServiceWorkerCacheErrorNotFound; case CACHE_STORAGE_ERROR_NOT_FOUND: return blink::WebServiceWorkerCacheErrorNotFound; + case CACHE_STORAGE_ERROR_QUOTA_EXCEEDED: + return blink::WebServiceWorkerCacheErrorQuotaExceeded; + case CACHE_STORAGE_ERROR_CACHE_NAME_NOT_FOUND: + return blink::WebServiceWorkerCacheErrorCacheNameNotFound; } NOTREACHED(); return blink::WebServiceWorkerCacheErrorNotImplemented; } -bool OriginCanAccessCacheStorage(const GURL& url) { - return IsOriginSecure(url); +bool OriginCanAccessCacheStorage(const url::Origin& origin) { + return !origin.unique() && IsOriginSecure(GURL(origin.Serialize())); } } // namespace @@ -67,7 +73,7 @@ void CacheStorageDispatcherHost::Init(CacheStorageContextImpl* context) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&CacheStorageDispatcherHost::CreateCacheListener, this, - make_scoped_refptr(context))); + base::RetainedRef(context))); } void CacheStorageDispatcherHost::OnDestruct() const { @@ -110,7 +116,7 @@ void CacheStorageDispatcherHost::CreateCacheListener( void CacheStorageDispatcherHost::OnCacheStorageHas( int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name) { TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageHas"); if (!OriginCanAccessCacheStorage(origin)) { @@ -118,7 +124,7 @@ void CacheStorageDispatcherHost::OnCacheStorageHas( return; } context_->cache_manager()->HasCache( - origin, base::UTF16ToUTF8(cache_name), + GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageHasCallback, this, thread_id, request_id)); } @@ -126,7 +132,7 @@ void CacheStorageDispatcherHost::OnCacheStorageHas( void CacheStorageDispatcherHost::OnCacheStorageOpen( int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name) { TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageOpen"); @@ -135,7 +141,7 @@ void CacheStorageDispatcherHost::OnCacheStorageOpen( return; } context_->cache_manager()->OpenCache( - origin, base::UTF16ToUTF8(cache_name), + GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageOpenCallback, this, thread_id, request_id)); } @@ -143,7 +149,7 @@ void CacheStorageDispatcherHost::OnCacheStorageOpen( void CacheStorageDispatcherHost::OnCacheStorageDelete( int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name) { TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageDelete"); @@ -152,14 +158,14 @@ void CacheStorageDispatcherHost::OnCacheStorageDelete( return; } context_->cache_manager()->DeleteCache( - origin, base::UTF16ToUTF8(cache_name), + GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageDeleteCallback, this, thread_id, request_id)); } void CacheStorageDispatcherHost::OnCacheStorageKeys(int thread_id, int request_id, - const GURL& origin) { + const url::Origin& origin) { TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageKeys"); if (!OriginCanAccessCacheStorage(origin)) { @@ -167,7 +173,7 @@ void CacheStorageDispatcherHost::OnCacheStorageKeys(int thread_id, return; } context_->cache_manager()->EnumerateCaches( - origin, + GURL(origin.Serialize()), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageKeysCallback, this, thread_id, request_id)); } @@ -175,7 +181,7 @@ void CacheStorageDispatcherHost::OnCacheStorageKeys(int thread_id, void CacheStorageDispatcherHost::OnCacheStorageMatch( int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const ServiceWorkerFetchRequest& request, const CacheStorageCacheQueryParams& match_params) { TRACE_EVENT0("CacheStorage", @@ -191,13 +197,13 @@ void CacheStorageDispatcherHost::OnCacheStorageMatch( if (match_params.cache_name.empty()) { context_->cache_manager()->MatchAllCaches( - origin, std::move(scoped_request), + GURL(origin.Serialize()), std::move(scoped_request), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageMatchCallback, this, thread_id, request_id)); return; } context_->cache_manager()->MatchCache( - origin, base::UTF16ToUTF8(match_params.cache_name), + GURL(origin.Serialize()), base::UTF16ToUTF8(match_params.cache_name), std::move(scoped_request), base::Bind(&CacheStorageDispatcherHost::OnCacheStorageMatchCallback, this, thread_id, request_id)); @@ -242,6 +248,7 @@ void CacheStorageDispatcherHost::OnCacheMatchAll( scoped_refptr cache = it->second; if (request.url.is_empty()) { cache->MatchAll( + scoped_ptr(), match_params, base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallback, this, thread_id, request_id, cache)); return; @@ -251,6 +258,13 @@ void CacheStorageDispatcherHost::OnCacheMatchAll( new ServiceWorkerFetchRequest(request.url, request.method, request.headers, request.referrer, request.is_reload)); + if (match_params.ignore_search) { + cache->MatchAll( + std::move(scoped_request), match_params, + base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallback, this, + thread_id, request_id, cache)); + return; + } cache->Match( std::move(scoped_request), base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallbackAdapter, @@ -322,14 +336,14 @@ void CacheStorageDispatcherHost::OnCacheStorageHasCallback( void CacheStorageDispatcherHost::OnCacheStorageOpenCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error) { if (error != CACHE_STORAGE_OK) { Send(new CacheStorageMsg_CacheStorageOpenError( thread_id, request_id, ToWebServiceWorkerCacheError(error))); return; } - CacheID cache_id = StoreCacheReference(cache); + CacheID cache_id = StoreCacheReference(std::move(cache)); Send(new CacheStorageMsg_CacheStorageOpenSuccess(thread_id, request_id, cache_id)); } @@ -388,7 +402,7 @@ void CacheStorageDispatcherHost::OnCacheStorageMatchCallback( void CacheStorageDispatcherHost::OnCacheMatchCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr response, scoped_ptr blob_data_handle) { @@ -407,7 +421,7 @@ void CacheStorageDispatcherHost::OnCacheMatchCallback( void CacheStorageDispatcherHost::OnCacheMatchAllCallbackAdapter( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr response, scoped_ptr blob_data_handle) { @@ -421,14 +435,14 @@ void CacheStorageDispatcherHost::OnCacheMatchAllCallbackAdapter( if (blob_data_handle) blob_data_handles->push_back(*blob_data_handle); } - OnCacheMatchAllCallback(thread_id, request_id, cache, error, + OnCacheMatchAllCallback(thread_id, request_id, std::move(cache), error, std::move(responses), std::move(blob_data_handles)); } void CacheStorageDispatcherHost::OnCacheMatchAllCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr responses, scoped_ptr blob_data_handles) { @@ -448,7 +462,7 @@ void CacheStorageDispatcherHost::OnCacheMatchAllCallback( void CacheStorageDispatcherHost::OnCacheKeysCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr requests) { if (error != CACHE_STORAGE_OK) { @@ -472,7 +486,7 @@ void CacheStorageDispatcherHost::OnCacheKeysCallback( void CacheStorageDispatcherHost::OnCacheBatchCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error) { if (error != CACHE_STORAGE_OK) { Send(new CacheStorageMsg_CacheBatchError( @@ -485,9 +499,9 @@ void CacheStorageDispatcherHost::OnCacheBatchCallback( CacheStorageDispatcherHost::CacheID CacheStorageDispatcherHost::StoreCacheReference( - const scoped_refptr& cache) { + scoped_refptr cache) { int cache_id = next_cache_id_++; - id_to_cache_map_[cache_id] = cache; + id_to_cache_map_[cache_id] = std::move(cache); return cache_id; } diff --git a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h index 77435096db2..14cfe301efb 100644 --- a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h +++ b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h @@ -13,6 +13,10 @@ #include "content/browser/cache_storage/cache_storage.h" #include "content/public/browser/browser_message_filter.h" +namespace url { +class Origin; +} + namespace content { class CacheStorageContextImpl; @@ -49,20 +53,22 @@ class CONTENT_EXPORT CacheStorageDispatcherHost : public BrowserMessageFilter { // The message receiver functions for the CacheStorage API: void OnCacheStorageHas(int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name); void OnCacheStorageOpen(int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name); void OnCacheStorageDelete(int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const base::string16& cache_name); - void OnCacheStorageKeys(int thread_id, int request_id, const GURL& origin); + void OnCacheStorageKeys(int thread_id, + int request_id, + const url::Origin& origin); void OnCacheStorageMatch(int thread_id, int request_id, - const GURL& origin, + const url::Origin& origin, const ServiceWorkerFetchRequest& request, const CacheStorageCacheQueryParams& match_params); @@ -91,7 +97,7 @@ class CONTENT_EXPORT CacheStorageDispatcherHost : public BrowserMessageFilter { CacheStorageError error); void OnCacheStorageOpenCallback(int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error); void OnCacheStorageDeleteCallback(int thread_id, int request_id, @@ -112,21 +118,21 @@ class CONTENT_EXPORT CacheStorageDispatcherHost : public BrowserMessageFilter { void OnCacheMatchCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr response, scoped_ptr blob_data_handle); void OnCacheMatchAllCallbackAdapter( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr response, scoped_ptr blob_data_handle); void OnCacheMatchAllCallback( int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr> responses, scoped_ptr blob_data_handles); @@ -137,18 +143,18 @@ class CONTENT_EXPORT CacheStorageDispatcherHost : public BrowserMessageFilter { const CacheStorageCacheQueryParams& match_params); void OnCacheKeysCallback(int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error, scoped_ptr requests); void OnCacheBatchCallback(int thread_id, int request_id, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error); // Hangs onto a scoped_refptr for the cache if it isn't already doing so. // Returns a unique cache_id. Call DropCacheReference when the client is done // with this cache. - CacheID StoreCacheReference(const scoped_refptr& cache); + CacheID StoreCacheReference(scoped_refptr cache); void DropCacheReference(CacheID cache_id); // Stores blob handles while waiting for acknowledgement of receipt from the diff --git a/chromium/content/browser/cache_storage/cache_storage_manager.cc b/chromium/content/browser/cache_storage/cache_storage_manager.cc index cd02deb0a0f..be2c6d7c5b1 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager.cc +++ b/chromium/content/browser/cache_storage/cache_storage_manager.cc @@ -9,6 +9,7 @@ #include #include +#include "base/barrier_closure.h" #include "base/bind.h" #include "base/files/file_enumerator.h" #include "base/files/file_util.h" @@ -23,7 +24,7 @@ #include "content/browser/cache_storage/cache_storage_quota_client.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/public/browser/browser_thread.h" -#include "net/base/net_util.h" +#include "net/base/url_util.h" #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/common/database/database_identifier.h" #include "storage/common/quota/quota_status_code.h" @@ -42,11 +43,16 @@ void DeleteOriginDidDeleteDir( bool rv) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback.Run(rv ? storage::kQuotaStatusOk : storage::kQuotaErrorAbort); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, rv ? storage::kQuotaStatusOk + : storage::kQuotaErrorAbort)); } -std::set ListOriginsOnDisk(base::FilePath root_path) { - std::set origins; +// Open the various cache directories' index files and extract their origins and +// last modified times. +void ListOriginsAndLastModifiedOnTaskRunner( + std::vector* usages, + base::FilePath root_path) { base::FileEnumerator file_enum(root_path, false /* recursive */, base::FileEnumerator::DIRECTORIES); @@ -55,33 +61,28 @@ std::set ListOriginsOnDisk(base::FilePath root_path) { std::string protobuf; base::ReadFileToString(path.AppendASCII(CacheStorage::kIndexFileName), &protobuf); - CacheStorageIndex index; if (index.ParseFromString(protobuf)) { - if (index.has_origin()) - origins.insert(GURL(index.origin())); + if (index.has_origin()) { + base::File::Info file_info; + if (base::GetFileInfo(path, &file_info)) { + usages->push_back(CacheStorageUsageInfo( + GURL(index.origin()), 0 /* size */, file_info.last_modified)); + } + } } } - - return origins; } -std::vector GetAllOriginsUsageOnTaskRunner( - const base::FilePath root_path) { - std::vector entries; - const std::set origins = ListOriginsOnDisk(root_path); - entries.reserve(origins.size()); - for (const GURL& origin : origins) { - base::FilePath path = - CacheStorageManager::ConstructOriginPath(root_path, origin); - int64_t size = base::ComputeDirectorySize(path); - base::File::Info file_info; - base::Time last_modified; - if (base::GetFileInfo(path, &file_info)) - last_modified = file_info.last_modified; - entries.push_back(CacheStorageUsageInfo(origin, size, last_modified)); - } - return entries; +std::set ListOriginsOnTaskRunner(base::FilePath root_path) { + std::vector usages; + ListOriginsAndLastModifiedOnTaskRunner(&usages, root_path); + + std::set out_origins; + for (const CacheStorageUsageInfo& usage : usages) + out_origins.insert(usage.origin); + + return out_origins; } void GetOriginsForHostDidListOrigins( @@ -93,26 +94,45 @@ void GetOriginsForHostDidListOrigins( if (host == net::GetHostOrSpecFromURL(origin)) out_origins.insert(origin); } - callback.Run(out_origins); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, out_origins)); } void EmptyQuotaStatusCallback(storage::QuotaStatusCode code) {} +void AllOriginSizesReported( + scoped_ptr> usages, + const CacheStorageContext::GetUsageInfoCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, *usages)); +} + +void OneOriginSizeReported(const base::Closure& callback, + CacheStorageUsageInfo* usage, + int64_t size) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + usage->total_size_bytes = size; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); +} + } // namespace // static scoped_ptr CacheStorageManager::Create( const base::FilePath& path, - const scoped_refptr& cache_task_runner, - const scoped_refptr& quota_manager_proxy) { + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy) { base::FilePath root_path = path; if (!path.empty()) { root_path = path.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) .AppendASCII("CacheStorage"); } - return make_scoped_ptr(new CacheStorageManager(root_path, cache_task_runner, - quota_manager_proxy)); + return make_scoped_ptr(new CacheStorageManager( + root_path, std::move(cache_task_runner), std::move(quota_manager_proxy))); } // static @@ -191,14 +211,14 @@ void CacheStorageManager::MatchAllCaches( } void CacheStorageManager::SetBlobParametersForCache( - const scoped_refptr& request_context_getter, + scoped_refptr request_context_getter, base::WeakPtr blob_storage_context) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(cache_storage_map_.empty()); DCHECK(!request_context_getter_ || request_context_getter_.get() == request_context_getter.get()); DCHECK(!blob_context_ || blob_context_.get() == blob_storage_context.get()); - request_context_getter_ = request_context_getter; + request_context_getter_ = std::move(request_context_getter); blob_context_ = blob_storage_context; } @@ -206,23 +226,54 @@ void CacheStorageManager::GetAllOriginsUsage( const CacheStorageContext::GetUsageInfoCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + scoped_ptr> usages( + new std::vector()); + if (IsMemoryBacked()) { - std::vector entries; - entries.reserve(cache_storage_map_.size()); for (const auto& origin_details : cache_storage_map_) { - entries.push_back(CacheStorageUsageInfo( - origin_details.first, origin_details.second->MemoryBackedSize(), - base::Time())); + usages->push_back( + CacheStorageUsageInfo(origin_details.first, 0 /* size */, + base::Time() /* last modified */)); } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(callback, entries)); + GetAllOriginsUsageGetSizes(std::move(usages), callback); return; } - PostTaskAndReplyWithResult( - cache_task_runner_.get(), FROM_HERE, - base::Bind(&GetAllOriginsUsageOnTaskRunner, root_path_), - base::Bind(callback)); + std::vector* usages_ptr = usages.get(); + cache_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&ListOriginsAndLastModifiedOnTaskRunner, usages_ptr, + root_path_), + base::Bind(&CacheStorageManager::GetAllOriginsUsageGetSizes, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(usages)), callback)); +} + +void CacheStorageManager::GetAllOriginsUsageGetSizes( + scoped_ptr> usages, + const CacheStorageContext::GetUsageInfoCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(usages); + + // The origin GURL and last modified times are set in |usages| but not the + // size in bytes. Call each CacheStorage's Size() function to fill that out. + std::vector* usages_ptr = usages.get(); + + if (usages->empty()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, *usages)); + return; + } + + base::Closure barrier_closure = base::BarrierClosure( + usages_ptr->size(), + base::Bind(&AllOriginSizesReported, base::Passed(std::move(usages)), + callback)); + + for (CacheStorageUsageInfo& usage : *usages_ptr) { + CacheStorage* cache_storage = FindOrCreateCacheStorage(usage.origin); + cache_storage->Size( + base::Bind(&OneOriginSizeReported, barrier_closure, &usage)); + } } void CacheStorageManager::GetOriginUsage( @@ -230,20 +281,9 @@ void CacheStorageManager::GetOriginUsage( const storage::QuotaClient::GetUsageCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (IsMemoryBacked()) { - int64_t usage = 0; - if (ContainsKey(cache_storage_map_, origin_url)) - usage = cache_storage_map_[origin_url]->MemoryBackedSize(); - callback.Run(usage); - return; - } + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin_url); - MigrateOrigin(origin_url); - PostTaskAndReplyWithResult( - cache_task_runner_.get(), FROM_HERE, - base::Bind(base::ComputeDirectorySize, - ConstructOriginPath(root_path_, origin_url)), - base::Bind(callback)); + cache_storage->Size(callback); } void CacheStorageManager::GetOrigins( @@ -255,12 +295,13 @@ void CacheStorageManager::GetOrigins( for (const auto& key_value : cache_storage_map_) origins.insert(key_value.first); - callback.Run(origins); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, origins)); return; } PostTaskAndReplyWithResult(cache_task_runner_.get(), FROM_HERE, - base::Bind(&ListOriginsOnDisk, root_path_), + base::Bind(&ListOriginsOnTaskRunner, root_path_), base::Bind(callback)); } @@ -275,13 +316,14 @@ void CacheStorageManager::GetOriginsForHost( if (host == net::GetHostOrSpecFromURL(key_value.first)) origins.insert(key_value.first); } - callback.Run(origins); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, origins)); return; } PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, - base::Bind(&ListOriginsOnDisk, root_path_), + base::Bind(&ListOriginsOnTaskRunner, root_path_), base::Bind(&GetOriginsForHostDidListOrigins, host, callback)); } @@ -290,18 +332,18 @@ void CacheStorageManager::DeleteOriginData( const storage::QuotaClient::DeletionCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + // Create the CacheStorage for the origin if it hasn't been loaded yet. + FindOrCreateCacheStorage(origin); + CacheStorageMap::iterator it = cache_storage_map_.find(origin); - if (it == cache_storage_map_.end()) { - callback.Run(storage::kQuotaStatusOk); - return; - } + DCHECK(it != cache_storage_map_.end()); CacheStorage* cache_storage = it->second.release(); cache_storage_map_.erase(origin); - cache_storage->CloseAllCaches( - base::Bind(&CacheStorageManager::DeleteOriginDidClose, origin, callback, - base::Passed(make_scoped_ptr(cache_storage)), - weak_ptr_factory_.GetWeakPtr())); + cache_storage->GetSizeThenCloseAllCaches( + base::Bind(&CacheStorageManager::DeleteOriginDidClose, + weak_ptr_factory_.GetWeakPtr(), origin, callback, + base::Passed(make_scoped_ptr(cache_storage)))); } void CacheStorageManager::DeleteOriginData(const GURL& origin) { @@ -309,42 +351,40 @@ void CacheStorageManager::DeleteOriginData(const GURL& origin) { DeleteOriginData(origin, base::Bind(&EmptyQuotaStatusCallback)); } -// static void CacheStorageManager::DeleteOriginDidClose( const GURL& origin, const storage::QuotaClient::DeletionCallback& callback, scoped_ptr cache_storage, - base::WeakPtr cache_manager) { + int64_t origin_size) { // TODO(jkarlin): Deleting the storage leaves any unfinished operations - // hanging, resulting in unresolved promises. Fix this by guaranteeing that - // callbacks are called in ServiceWorkerStorage. + // hanging, resulting in unresolved promises. Fix this by returning early from + // CacheStorage operations posted after GetSizeThenCloseAllCaches is called. cache_storage.reset(); - if (!cache_manager) { - callback.Run(storage::kQuotaErrorAbort); - return; - } + quota_manager_proxy_->NotifyStorageModified( + storage::QuotaClient::kServiceWorkerCache, origin, + storage::kStorageTypeTemporary, -1 * origin_size); - if (cache_manager->IsMemoryBacked()) { - callback.Run(storage::kQuotaStatusOk); + if (IsMemoryBacked()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, storage::kQuotaStatusOk)); return; } - cache_manager->MigrateOrigin(origin); + MigrateOrigin(origin); PostTaskAndReplyWithResult( - cache_manager->cache_task_runner_.get(), FROM_HERE, - base::Bind(&DeleteDir, - ConstructOriginPath(cache_manager->root_path_, origin)), + cache_task_runner_.get(), FROM_HERE, + base::Bind(&DeleteDir, ConstructOriginPath(root_path_, origin)), base::Bind(&DeleteOriginDidDeleteDir, callback)); } CacheStorageManager::CacheStorageManager( const base::FilePath& path, - const scoped_refptr& cache_task_runner, - const scoped_refptr& quota_manager_proxy) + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy) : root_path_(path), - cache_task_runner_(cache_task_runner), - quota_manager_proxy_(quota_manager_proxy), + cache_task_runner_(std::move(cache_task_runner)), + quota_manager_proxy_(std::move(quota_manager_proxy)), weak_ptr_factory_(this) { if (quota_manager_proxy_.get()) { quota_manager_proxy_->RegisterClient( diff --git a/chromium/content/browser/cache_storage/cache_storage_manager.h b/chromium/content/browser/cache_storage/cache_storage_manager.h index 121a25228d3..ded3703e924 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager.h +++ b/chromium/content/browser/cache_storage/cache_storage_manager.h @@ -40,8 +40,8 @@ class CONTENT_EXPORT CacheStorageManager { public: static scoped_ptr Create( const base::FilePath& path, - const scoped_refptr& cache_task_runner, - const scoped_refptr& quota_manager_proxy); + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy); static scoped_ptr Create( CacheStorageManager* old_manager); @@ -76,7 +76,7 @@ class CONTENT_EXPORT CacheStorageManager { // This must be called before creating any of the public *Cache functions // above. void SetBlobParametersForCache( - const scoped_refptr& request_context_getter, + scoped_refptr request_context_getter, base::WeakPtr blob_storage_context); base::WeakPtr AsWeakPtr() { @@ -95,8 +95,8 @@ class CONTENT_EXPORT CacheStorageManager { CacheStorageManager( const base::FilePath& path, - const scoped_refptr& cache_task_runner, - const scoped_refptr& quota_manager_proxy); + scoped_refptr cache_task_runner, + scoped_refptr quota_manager_proxy); // The returned CacheStorage* is owned by this manager. CacheStorage* FindOrCreateCacheStorage(const GURL& origin); @@ -104,6 +104,10 @@ class CONTENT_EXPORT CacheStorageManager { // QuotaClient and Browsing Data Deletion support void GetAllOriginsUsage( const CacheStorageContext::GetUsageInfoCallback& callback); + void GetAllOriginsUsageGetSizes( + scoped_ptr> usage_info, + const CacheStorageContext::GetUsageInfoCallback& callback); + void GetOriginUsage(const GURL& origin_url, const storage::QuotaClient::GetUsageCallback& callback); void GetOrigins(const storage::QuotaClient::GetOriginsCallback& callback); @@ -113,11 +117,11 @@ class CONTENT_EXPORT CacheStorageManager { void DeleteOriginData(const GURL& origin, const storage::QuotaClient::DeletionCallback& callback); void DeleteOriginData(const GURL& origin); - static void DeleteOriginDidClose( + void DeleteOriginDidClose( const GURL& origin, const storage::QuotaClient::DeletionCallback& callback, scoped_ptr cache_storage, - base::WeakPtr cache_manager); + int64_t origin_size); scoped_refptr url_request_context_getter() const { @@ -128,7 +132,7 @@ class CONTENT_EXPORT CacheStorageManager { return blob_context_; } - const scoped_refptr& cache_task_runner() const { + scoped_refptr cache_task_runner() const { return cache_task_runner_; } diff --git a/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc index f8d94976e2b..dec8cfa953f 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc @@ -24,6 +24,7 @@ #include "content/browser/quota/mock_quota_manager_proxy.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/cache_storage_usage_info.h" +#include "content/public/test/mock_special_storage_policy.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/url_request/url_request_context.h" @@ -75,19 +76,25 @@ class CacheStorageManagerTest : public testing::Test { url_request_context->set_job_factory(url_request_job_factory_.get()); + if (!MemoryOnly()) + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + quota_policy_ = new MockSpecialStoragePolicy; + mock_quota_manager_ = new MockQuotaManager( + MemoryOnly() /* is incognito */, temp_dir_.path(), + base::ThreadTaskRunnerHandle::Get().get(), + base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get()); + mock_quota_manager_->SetQuota( + GURL(origin1_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); + mock_quota_manager_->SetQuota( + GURL(origin2_), storage::kStorageTypeTemporary, 1024 * 1024 * 100); + quota_manager_proxy_ = new MockQuotaManagerProxy( - nullptr, base::ThreadTaskRunnerHandle::Get().get()); + mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); - if (MemoryOnly()) { - cache_manager_ = CacheStorageManager::Create( - base::FilePath(), base::ThreadTaskRunnerHandle::Get(), - quota_manager_proxy_); - } else { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - cache_manager_ = CacheStorageManager::Create( - temp_dir_.path(), base::ThreadTaskRunnerHandle::Get(), - quota_manager_proxy_); - } + cache_manager_ = CacheStorageManager::Create( + temp_dir_.path(), base::ThreadTaskRunnerHandle::Get(), + quota_manager_proxy_); cache_manager_->SetBlobParametersForCache( browser_context_.GetRequestContext(), @@ -110,9 +117,9 @@ class CacheStorageManagerTest : public testing::Test { } void CacheAndErrorCallback(base::RunLoop* run_loop, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error) { - callback_cache_ = cache; + callback_cache_ = std::move(cache); callback_error_ = error; run_loop->Quit(); } @@ -219,8 +226,13 @@ class CacheStorageManagerTest : public testing::Test { return callback_error_ == CACHE_STORAGE_OK; } - bool CachePut(const scoped_refptr& cache, - const GURL& url) { + bool CachePut(CacheStorageCache* cache, const GURL& url) { + return CachePutWithStatusCode(cache, url, 200); + } + + bool CachePutWithStatusCode(CacheStorageCache* cache, + const GURL& url, + int status_code) { ServiceWorkerFetchRequest request; request.url = url; @@ -231,9 +243,11 @@ class CacheStorageManagerTest : public testing::Test { scoped_ptr blob_handle = blob_storage_context_->AddFinishedBlob(blob_data.get()); ServiceWorkerResponse response( - url, 200, "OK", blink::WebServiceWorkerResponseTypeDefault, + url, status_code, "OK", blink::WebServiceWorkerResponseTypeDefault, ServiceWorkerHeaderMap(), blob_handle->uuid(), url.spec().size(), - GURL(), blink::WebServiceWorkerResponseErrorUnknown); + GURL(), blink::WebServiceWorkerResponseErrorUnknown, base::Time(), + false /* is_in_cache_storage */, + std::string() /* cache_storage_cache_name */); CacheStorageBatchOperation operation; operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; @@ -250,8 +264,7 @@ class CacheStorageManagerTest : public testing::Test { return callback_error_ == CACHE_STORAGE_OK; } - bool CacheMatch(const scoped_refptr& cache, - const GURL& url) { + bool CacheMatch(CacheStorageCache* cache, const GURL& url) { scoped_ptr request( new ServiceWorkerFetchRequest()); request->url = url; @@ -298,6 +311,25 @@ class CacheStorageManagerTest : public testing::Test { run_loop->Quit(); } + int64_t GetSizeThenCloseAllCaches(const GURL& origin) { + base::RunLoop loop; + CacheStorage* cache_storage = CacheStorageForOrigin(origin); + cache_storage->GetSizeThenCloseAllCaches( + base::Bind(&CacheStorageManagerTest::UsageCallback, + base::Unretained(this), &loop)); + loop.Run(); + return callback_usage_; + } + + int64_t Size(const GURL& origin) { + base::RunLoop loop; + CacheStorage* cache_storage = CacheStorageForOrigin(origin); + cache_storage->Size(base::Bind(&CacheStorageManagerTest::UsageCallback, + base::Unretained(this), &loop)); + loop.Run(); + return callback_usage_; + } + protected: // Temporary directory must be allocated first so as to be destroyed last. base::ScopedTempDir temp_dir_; @@ -307,6 +339,8 @@ class CacheStorageManagerTest : public testing::Test { scoped_ptr url_request_job_factory_; storage::BlobStorageContext* blob_storage_context_; + scoped_refptr quota_policy_; + scoped_refptr mock_quota_manager_; scoped_refptr quota_manager_proxy_; scoped_ptr cache_manager_; @@ -395,6 +429,18 @@ TEST_P(CacheStorageManagerTestP, DeleteTwice) { EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); } +TEST_P(CacheStorageManagerTestP, DeleteCacheReducesOriginSize) { + EXPECT_TRUE(Open(origin1_, "foo")); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + // The quota manager gets updated after the put operation runs its callback so + // run the event loop. + base::RunLoop().RunUntilIdle(); + int64_t put_delta = quota_manager_proxy_->last_notified_delta(); + EXPECT_LT(0, put_delta); + EXPECT_TRUE(Delete(origin1_, "foo")); + EXPECT_EQ(put_delta, -1 * quota_manager_proxy_->last_notified_delta()); +} + TEST_P(CacheStorageManagerTestP, EmptyKeys) { EXPECT_TRUE(Keys(origin1_)); EXPECT_TRUE(callback_strings_.empty()); @@ -427,33 +473,33 @@ TEST_P(CacheStorageManagerTestP, DeletedKeysGone) { TEST_P(CacheStorageManagerTestP, StorageMatchEntryExists) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); } TEST_P(CacheStorageManagerTestP, StorageMatchNoEntry) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/bar"))); EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); } TEST_P(CacheStorageManagerTestP, StorageMatchNoCache) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_FALSE(StorageMatch(origin1_, "bar", GURL("http://example.com/foo"))); - EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); + EXPECT_EQ(CACHE_STORAGE_ERROR_CACHE_NAME_NOT_FOUND, callback_error_); } TEST_P(CacheStorageManagerTestP, StorageMatchAllEntryExists) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); } TEST_P(CacheStorageManagerTestP, StorageMatchAllNoEntry) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/bar"))); EXPECT_EQ(CACHE_STORAGE_ERROR_NOT_FOUND, callback_error_); } @@ -468,8 +514,8 @@ TEST_F(CacheStorageManagerTest, StorageReuseCacheName) { // with the same URL should work. (see crbug.com/542668) const GURL kTestURL = GURL("http://example.com/foo"); EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, kTestURL)); - EXPECT_TRUE(CacheMatch(callback_cache_, kTestURL)); + EXPECT_TRUE(CachePut(callback_cache_.get(), kTestURL)); + EXPECT_TRUE(CacheMatch(callback_cache_.get(), kTestURL)); scoped_ptr data_handle = std::move(callback_data_handle_); @@ -477,22 +523,28 @@ TEST_F(CacheStorageManagerTest, StorageReuseCacheName) { // The cache is deleted but the handle to one of its entries is still // open. Creating a new cache in the same directory would fail on Windows. EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, kTestURL)); + EXPECT_TRUE(CachePut(callback_cache_.get(), kTestURL)); } TEST_P(CacheStorageManagerTestP, StorageMatchAllEntryExistsTwice) { EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePutWithStatusCode(callback_cache_.get(), + GURL("http://example.com/foo"), 200)); EXPECT_TRUE(Open(origin1_, "bar")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePutWithStatusCode(callback_cache_.get(), + GURL("http://example.com/foo"), 201)); EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); + + // The caches need to be searched in order of creation, so verify that the + // response came from the first cache. + EXPECT_EQ(200, callback_cache_response_->status_code); } TEST_P(CacheStorageManagerTestP, StorageMatchInOneOfMany) { EXPECT_TRUE(Open(origin1_, "foo")); EXPECT_TRUE(Open(origin1_, "bar")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_TRUE(Open(origin1_, "baz")); EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); @@ -595,56 +647,67 @@ TEST_P(CacheStorageManagerTestP, OpenRunsSerially) { TEST_P(CacheStorageManagerTestP, GetOriginUsage) { EXPECT_EQ(0, GetOriginUsage(origin1_)); EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_EQ(0, GetOriginUsage(origin1_)); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + int64_t foo_size = GetOriginUsage(origin1_); EXPECT_LT(0, GetOriginUsage(origin1_)); EXPECT_EQ(0, GetOriginUsage(origin2_)); + + // Add the same entry into a second cache, the size should double. + EXPECT_TRUE(Open(origin1_, "bar")); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + EXPECT_EQ(2 * foo_size, GetOriginUsage(origin1_)); } TEST_P(CacheStorageManagerTestP, GetAllOriginsUsage) { EXPECT_EQ(0ULL, GetAllOriginsUsage().size()); + // Put one entry in a cache on origin 1. EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + + // Put two entries (of identical size) in a cache on origin 2. + EXPECT_TRUE(Open(origin2_, "foo")); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/bar"))); + std::vector usage = GetAllOriginsUsage(); - EXPECT_EQ(1ULL, usage.size()); - const CacheStorageUsageInfo& info = usage[0]; - EXPECT_EQ(origin1_, info.origin); - EXPECT_LT(0, info.total_size_bytes); - if (MemoryOnly()) - EXPECT_TRUE(info.last_modified.is_null()); - else - EXPECT_FALSE(info.last_modified.is_null()); + EXPECT_EQ(2ULL, usage.size()); + + int origin1_index = usage[0].origin == origin1_ ? 0 : 1; + int origin2_index = usage[1].origin == origin2_ ? 1 : 0; + EXPECT_NE(origin1_index, origin2_index); + + int64_t origin1_size = usage[origin1_index].total_size_bytes; + int64_t origin2_size = usage[origin2_index].total_size_bytes; + EXPECT_EQ(2 * origin1_size, origin2_size); + + if (MemoryOnly()) { + EXPECT_TRUE(usage[origin1_index].last_modified.is_null()); + EXPECT_TRUE(usage[origin2_index].last_modified.is_null()); + } else { + EXPECT_FALSE(usage[origin1_index].last_modified.is_null()); + EXPECT_FALSE(usage[origin2_index].last_modified.is_null()); + } } -TEST_F(CacheStorageManagerMemoryOnlyTest, MemoryBackedSize) { - CacheStorage* cache_storage = CacheStorageForOrigin(origin1_); - EXPECT_EQ(0, cache_storage->MemoryBackedSize()); - +TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCaches) { EXPECT_TRUE(Open(origin1_, "foo")); - scoped_refptr foo_cache = callback_cache_; + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo2"))); EXPECT_TRUE(Open(origin1_, "bar")); - scoped_refptr bar_cache = callback_cache_; - EXPECT_EQ(0, cache_storage->MemoryBackedSize()); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/bar"))); - EXPECT_TRUE(CachePut(foo_cache, GURL("http://example.com/foo"))); - EXPECT_LT(0, cache_storage->MemoryBackedSize()); - int64_t foo_size = cache_storage->MemoryBackedSize(); + int64_t origin_size = GetOriginUsage(origin1_); + EXPECT_LT(0, origin_size); - EXPECT_TRUE(CachePut(bar_cache, GURL("http://example.com/foo"))); - EXPECT_EQ(foo_size * 2, cache_storage->MemoryBackedSize()); -} - -TEST_F(CacheStorageManagerTest, MemoryBackedSizePersistent) { - CacheStorage* cache_storage = CacheStorageForOrigin(origin1_); - EXPECT_EQ(0, cache_storage->MemoryBackedSize()); - EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); - EXPECT_EQ(0, cache_storage->MemoryBackedSize()); + EXPECT_EQ(origin_size, GetSizeThenCloseAllCaches(origin1_)); + EXPECT_FALSE(CachePut(callback_cache_.get(), GURL("http://example.com/baz"))); } TEST_F(CacheStorageManagerTest, DeleteUnreferencedCacheDirectories) { // Create a referenced cache. EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); // Create an unreferenced directory next to the referenced one. base::FilePath origin_path = CacheStorageManager::ConstructOriginPath( @@ -660,12 +723,63 @@ TEST_F(CacheStorageManagerTest, DeleteUnreferencedCacheDirectories) { // Verify that the referenced cache still works. EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CacheMatch(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE( + CacheMatch(callback_cache_.get(), GURL("http://example.com/foo"))); // Verify that the unreferenced cache is gone. EXPECT_FALSE(base::DirectoryExists(unreferenced_path)); } +TEST_P(CacheStorageManagerTestP, OpenCacheStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_TRUE(Open(origin1_, "foo")); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, HasStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_FALSE(Has(origin1_, "foo")); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, DeleteStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_FALSE(Delete(origin1_, "foo")); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, KeysStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, MatchCacheStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, MatchAllCachesStorageAccessed) { + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); + EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); + EXPECT_EQ(1, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, SizeStorageAccessed) { + EXPECT_EQ(0, Size(origin1_)); + // Size is not part of the web API and should not notify the quota manager of + // an access. + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); +} + +TEST_P(CacheStorageManagerTestP, SizeThenCloseStorageAccessed) { + EXPECT_EQ(0, GetSizeThenCloseAllCaches(origin1_)); + // GetSizeThenCloseAllCaches is not part of the web API and should not notify + // the quota manager of an access. + EXPECT_EQ(0, quota_manager_proxy_->notify_storage_accessed_count()); +} + class CacheStorageMigrationTest : public CacheStorageManagerTest { protected: CacheStorageMigrationTest() : cache1_("foo"), cache2_("bar") {} @@ -729,7 +843,7 @@ TEST_F(CacheStorageMigrationTest, DeleteCache) { } TEST_F(CacheStorageMigrationTest, GetOriginUsage) { - EXPECT_GT(GetOriginUsage(origin1_), 0); + EXPECT_EQ(0, GetOriginUsage(origin1_)); EXPECT_FALSE(base::DirectoryExists(legacy_path_)); EXPECT_TRUE(base::DirectoryExists(new_path_)); } @@ -774,7 +888,7 @@ class MigratedLegacyCacheDirectoryNameTest : public CacheStorageManagerTest { // Populate a legacy cache. ASSERT_TRUE(Open(origin1_, legacy_cache_name_)); - EXPECT_TRUE(CachePut(callback_cache_, stored_url_)); + EXPECT_TRUE(CachePut(callback_cache_.get(), stored_url_)); base::FilePath new_path = callback_cache_->path(); // Close the cache's backend so that the files can be moved. @@ -827,27 +941,29 @@ TEST_F(MigratedLegacyCacheDirectoryNameTest, LegacyCacheMigrated) { ASSERT_FALSE(base::DirectoryExists(legacy_path_)); // Verify that the existing entry still works. - EXPECT_TRUE(CacheMatch(callback_cache_, stored_url_)); + EXPECT_TRUE(CacheMatch(callback_cache_.get(), stored_url_)); // Verify that adding new entries works. - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo2"))); - EXPECT_TRUE(CacheMatch(callback_cache_, GURL("http://example.com/foo2"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo2"))); + EXPECT_TRUE( + CacheMatch(callback_cache_.get(), GURL("http://example.com/foo2"))); } TEST_F(MigratedLegacyCacheDirectoryNameTest, RandomDirectoryCacheSideBySideWithLegacy) { EXPECT_TRUE(Open(origin1_, legacy_cache_name_)); EXPECT_TRUE(Open(origin1_, "bar")); - EXPECT_TRUE(CachePut(callback_cache_, stored_url_)); - EXPECT_TRUE(CacheMatch(callback_cache_, stored_url_)); + EXPECT_TRUE(CachePut(callback_cache_.get(), stored_url_)); + EXPECT_TRUE(CacheMatch(callback_cache_.get(), stored_url_)); } TEST_F(MigratedLegacyCacheDirectoryNameTest, DeleteLegacyCacheAndRecreateNew) { EXPECT_TRUE(Delete(origin1_, legacy_cache_name_)); EXPECT_TRUE(Open(origin1_, legacy_cache_name_)); - EXPECT_FALSE(CacheMatch(callback_cache_, stored_url_)); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo2"))); - EXPECT_TRUE(CacheMatch(callback_cache_, GURL("http://example.com/foo2"))); + EXPECT_FALSE(CacheMatch(callback_cache_.get(), stored_url_)); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo2"))); + EXPECT_TRUE( + CacheMatch(callback_cache_.get(), GURL("http://example.com/foo2"))); } class CacheStorageQuotaClientTest : public CacheStorageManagerTest { @@ -929,6 +1045,11 @@ class CacheStorageQuotaClientTest : public CacheStorageManagerTest { DISALLOW_COPY_AND_ASSIGN(CacheStorageQuotaClientTest); }; +class CacheStorageQuotaClientDiskOnlyTest : public CacheStorageQuotaClientTest { + public: + bool MemoryOnly() override { return false; } +}; + class CacheStorageQuotaClientTestP : public CacheStorageQuotaClientTest, public testing::WithParamInterface { bool MemoryOnly() override { return !GetParam(); } @@ -941,7 +1062,7 @@ TEST_P(CacheStorageQuotaClientTestP, QuotaID) { TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginUsage) { EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); EXPECT_TRUE(Open(origin1_, "foo")); - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_LT(0, QuotaGetOriginUsage(origin1_)); } @@ -967,14 +1088,20 @@ TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForHost) { } TEST_P(CacheStorageQuotaClientTestP, QuotaDeleteOriginData) { + EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); EXPECT_TRUE(Open(origin1_, "foo")); // Call put to test that initialized caches are properly deleted too. - EXPECT_TRUE(CachePut(callback_cache_, GURL("http://example.com/foo"))); + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); EXPECT_TRUE(Open(origin1_, "bar")); EXPECT_TRUE(Open(origin2_, "baz")); + int64_t origin1_size = QuotaGetOriginUsage(origin1_); + EXPECT_LT(0, origin1_size); + EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); + EXPECT_EQ(-1 * origin1_size, quota_manager_proxy_->last_notified_delta()); + EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); EXPECT_FALSE(Has(origin1_, "foo")); EXPECT_FALSE(Has(origin1_, "bar")); EXPECT_TRUE(Has(origin2_, "baz")); @@ -985,6 +1112,25 @@ TEST_P(CacheStorageQuotaClientTestP, QuotaDeleteEmptyOrigin) { EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); } +TEST_F(CacheStorageQuotaClientDiskOnlyTest, QuotaDeleteUnloadedOriginData) { + EXPECT_TRUE(Open(origin1_, "foo")); + // Call put to test that initialized caches are properly deleted too. + EXPECT_TRUE(CachePut(callback_cache_.get(), GURL("http://example.com/foo"))); + + // Close the cache backend so that it writes out its index to disk. + base::RunLoop run_loop; + callback_cache_->Close(run_loop.QuitClosure()); + run_loop.Run(); + + // Create a new CacheStorageManager that hasn't yet loaded the origin. + quota_manager_proxy_->SimulateQuotaManagerDestroyed(); + cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); + quota_client_.reset(new CacheStorageQuotaClient(cache_manager_->AsWeakPtr())); + + EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); + EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); +} + TEST_P(CacheStorageQuotaClientTestP, QuotaDoesSupport) { EXPECT_TRUE(QuotaDoesSupport(storage::kStorageTypeTemporary)); EXPECT_FALSE(QuotaDoesSupport(storage::kStorageTypePersistent)); diff --git a/chromium/content/browser/cache_storage/cache_storage_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_unittest.cc index faf0eb10635..9c0081ba087 100644 --- a/chromium/content/browser/cache_storage/cache_storage_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_unittest.cc @@ -7,9 +7,12 @@ #include "base/bind.h" #include "base/callback.h" #include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/thread_task_runner_handle.h" +#include "content/browser/quota/mock_quota_manager_proxy.h" +#include "content/public/test/mock_special_storage_policy.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/url_request/url_request_context_getter.h" #include "storage/browser/quota/quota_manager_proxy.h" @@ -23,12 +26,14 @@ const char kOrigin[] = "http://example.com/"; class TestCacheStorage : public CacheStorage { public: - explicit TestCacheStorage(const base::FilePath& file_path) + TestCacheStorage( + const base::FilePath& file_path, + scoped_refptr quota_manager_proxy) : CacheStorage(file_path, false /* memory_only */, base::ThreadTaskRunnerHandle::Get().get(), scoped_refptr(), - scoped_refptr(), + quota_manager_proxy, base::WeakPtr(), GURL(kOrigin)), delay_preserved_cache_callback_(false) {} @@ -67,7 +72,17 @@ class CacheStorageTest : public testing::Test { void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - test_cache_storage_.reset(new TestCacheStorage(temp_dir_.path())); + + quota_policy_ = new MockSpecialStoragePolicy; + mock_quota_manager_ = new MockQuotaManager( + false /* is incognito */, temp_dir_.path(), + base::ThreadTaskRunnerHandle::Get().get(), + base::ThreadTaskRunnerHandle::Get().get(), quota_policy_.get()); + quota_manager_proxy_ = new MockQuotaManagerProxy( + mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); + + test_cache_storage_.reset( + new TestCacheStorage(temp_dir_.path(), quota_manager_proxy_)); } bool OpenCache(const std::string& cache_name) { @@ -81,7 +96,7 @@ class CacheStorageTest : public testing::Test { } void OpenCacheCallback(bool* callback_called, - const scoped_refptr& cache, + scoped_refptr cache, CacheStorageError error) { *callback_called = true; callback_cache_ = cache; @@ -90,6 +105,9 @@ class CacheStorageTest : public testing::Test { base::ScopedTempDir temp_dir_; TestBrowserThreadBundle browser_thread_bundle_; + scoped_refptr quota_policy_; + scoped_refptr mock_quota_manager_; + scoped_refptr quota_manager_proxy_; scoped_ptr test_cache_storage_; scoped_refptr callback_cache_; diff --git a/chromium/content/browser/child_process_launcher.cc b/chromium/content/browser/child_process_launcher.cc index 57a6368cb7d..47205b630c1 100644 --- a/chromium/content/browser/child_process_launcher.cc +++ b/chromium/content/browser/child_process_launcher.cc @@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" +#include "base/process/launch.h" #include "base/process/process.h" #include "base/strings/string_number_conversions.h" #include "base/synchronization/lock.h" @@ -23,9 +24,13 @@ #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" #include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" #if defined(OS_WIN) #include "base/files/file_path.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" #include "content/common/sandbox_win.h" #include "content/public/common/sandbox_init.h" #elif defined(OS_MACOSX) @@ -39,8 +44,10 @@ #elif defined(OS_POSIX) #include "base/memory/singleton.h" #include "content/browser/renderer_host/render_sandbox_host_linux.h" +#include "content/browser/zygote_host/zygote_communication_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" #include "content/common/child_process_sandbox_support_impl_linux.h" +#include "content/public/browser/zygote_handle_linux.h" #endif #if defined(OS_POSIX) @@ -53,9 +60,10 @@ namespace content { namespace { -typedef base::Callback NotifyCallback; @@ -79,6 +87,7 @@ void OnChildProcessStartedAndroid(const NotifyCallback& callback, BrowserThread::ID client_thread_id, const base::TimeTicks begin_launch_time, base::ScopedFD ipcfd, + base::ScopedFD mojo_fd, base::ProcessHandle handle) { // This can be called on the launcher thread or UI thread. base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; @@ -87,8 +96,8 @@ void OnChildProcessStartedAndroid(const NotifyCallback& callback, base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); base::Closure callback_on_client_thread( - base::Bind(callback, false, base::Passed(&ipcfd), - base::Passed(base::Process(handle)))); + base::Bind(callback, nullptr, base::Passed(&ipcfd), + base::Passed(&mojo_fd), base::Passed(base::Process(handle)))); if (BrowserThread::CurrentlyOn(client_thread_id)) { callback_on_client_thread.Run(); } else { @@ -105,18 +114,19 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, #if defined(OS_ANDROID) base::ScopedFD ipcfd, #endif + mojo::edk::ScopedPlatformHandle client_handle, base::CommandLine* cmd_line) { DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); scoped_ptr delegate_deleter(delegate); +#if !defined(OS_ANDROID) + ZygoteHandle zygote = nullptr; +#endif #if defined(OS_WIN) - bool use_zygote = false; bool launch_elevated = delegate->ShouldLaunchElevated(); #elif defined(OS_MACOSX) - bool use_zygote = false; base::EnvironmentMap env = delegate->GetEnvironment(); base::ScopedFD ipcfd = delegate->TakeIpcFd(); #elif defined(OS_POSIX) && !defined(OS_ANDROID) - bool use_zygote = delegate->ShouldUseZygote(); base::EnvironmentMap env = delegate->GetEnvironment(); base::ScopedFD ipcfd = delegate->TakeIpcFd(); #endif @@ -126,11 +136,19 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, base::Process process; #if defined(OS_WIN) if (launch_elevated) { + // TODO(rockot): We may want to support Mojo IPC to elevated processes as + // well, but this isn't currently feasible without sharing a pipe path on + // the command line as elevated process launch goes through ShellExecuteEx. base::LaunchOptions options; options.start_hidden = true; process = base::LaunchElevatedProcess(*cmd_line, options); } else { - process = StartSandboxedProcess(delegate, cmd_line); + base::HandlesToInheritVector handles; + handles.push_back(client_handle.get().handle); + cmd_line->AppendSwitchASCII( + mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch, + base::UintToString(base::win::HandleToUint32(handles[0]))); + process = StartSandboxedProcess(delegate, cmd_line, handles); } #elif defined(OS_POSIX) std::string process_type = @@ -138,10 +156,17 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, scoped_ptr files_to_register( FileDescriptorInfoImpl::Create()); + base::ScopedFD mojo_fd(client_handle.release().handle); + DCHECK(mojo_fd.is_valid()); + #if defined(OS_ANDROID) - files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); + if (ipcfd.get() != -1) + files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); + files_to_register->Share(kMojoIPCChannel, mojo_fd.get()); #else - files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); + if (ipcfd.get() != -1) + files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); + files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd)); #endif #endif @@ -154,6 +179,31 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, #endif ); #if defined(V8_USE_EXTERNAL_STARTUP_DATA) + bool snapshot_loaded = false; +#if defined(OS_ANDROID) + base::MemoryMappedFile::Region region; + auto maybe_register = [®ion, ®ions, &files_to_register](int key, + int fd) { + if (fd != -1) { + files_to_register->Share(key, fd); + regions.insert(std::make_pair(key, region)); + } + }; + maybe_register( + kV8NativesDataDescriptor32, + gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion, true)); + maybe_register( + kV8NativesDataDescriptor64, + gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion, false)); + maybe_register( + kV8SnapshotDataDescriptor32, + gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, true)); + maybe_register( + kV8SnapshotDataDescriptor64, + gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, false)); + + snapshot_loaded = true; +#else base::PlatformFile natives_pf = gin::V8Initializer::GetOpenNativesFileForChildProcesses( ®ions[kV8NativesDataDescriptor]); @@ -167,13 +217,15 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, // Failure to load the V8 snapshot is not necessarily an error. V8 can start // up (slower) without the snapshot. if (snapshot_pf != -1) { + snapshot_loaded = true; files_to_register->Share(kV8SnapshotDataDescriptor, snapshot_pf); regions.insert(std::make_pair(kV8SnapshotDataDescriptor, snapshot_region)); } +#endif if (process_type != switches::kZygoteProcess) { cmd_line->AppendSwitch(::switches::kV8NativesPassedByFD); - if (snapshot_pf != -1) { + if (snapshot_loaded) { cmd_line->AppendSwitch(::switches::kV8SnapshotPassedByFD); } } @@ -192,15 +244,24 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, StartChildProcess( cmd_line->argv(), child_process_id, std::move(files_to_register), regions, base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, - begin_launch_time, base::Passed(&ipcfd))); + begin_launch_time, base::Passed(&ipcfd), + base::Passed(&mojo_fd))); #elif defined(OS_POSIX) // We need to close the client end of the IPC channel to reliably detect // child termination. #if !defined(OS_MACOSX) - if (use_zygote) { - base::ProcessHandle handle = ZygoteHostImpl::GetInstance()->ForkRequest( + ZygoteHandle* zygote_handle = delegate->GetZygote(); + // If |zygote_handle| is null, a zygote should not be used. + if (zygote_handle) { + // This code runs on the PROCESS_LAUNCHER thread so race conditions are not + // an issue with the lazy initialization. + if (*zygote_handle == nullptr) { + *zygote_handle = CreateZygote(); + } + zygote = *zygote_handle; + base::ProcessHandle handle = zygote->ForkRequest( cmd_line->argv(), std::move(files_to_register), process_type); process = base::Process(handle); } else @@ -278,13 +339,11 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, begin_launch_time); } BrowserThread::PostTask(client_thread_id, FROM_HERE, - base::Bind(callback, - use_zygote, - base::Passed(&process))); + base::Bind(callback, zygote, base::Passed(&process))); #endif // !defined(OS_ANDROID) } -void TerminateOnLauncherThread(bool zygote, base::Process process) { +void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) { DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); #if defined(OS_ANDROID) VLOG(1) << "ChromeProcess: Stopping process with handle " @@ -300,7 +359,7 @@ void TerminateOnLauncherThread(bool zygote, base::Process process) { if (zygote) { // If the renderer was created via a zygote, we have to proxy the reaping // through the zygote process. - ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle()); + zygote->EnsureProcessTerminated(process.Handle()); } else #endif // !OS_MACOSX base::EnsureProcessTerminated(std::move(process)); @@ -330,7 +389,7 @@ ChildProcessLauncher::ChildProcessLauncher( : client_(client), termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), exit_code_(RESULT_CODE_NORMAL_EXIT), - zygote_(false), + zygote_(nullptr), starting_(true), #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ @@ -386,6 +445,8 @@ void ChildProcessLauncher::Launch( NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, weak_factory_.GetWeakPtr(), terminate_child_on_shutdown_)); + mojo::edk::ScopedPlatformHandle client_handle = + mojo_platform_channel_.PassClientHandle(); BrowserThread::PostTask( BrowserThread::PROCESS_LAUNCHER, FROM_HERE, base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, @@ -393,15 +454,15 @@ void ChildProcessLauncher::Launch( #if defined(OS_ANDROID) base::Passed(&ipcfd), #endif - cmd_line)); + base::Passed(&client_handle), cmd_line)); } void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { DCHECK(CalledOnValidThread()); #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) if (zygote_) { - termination_status_ = ZygoteHostImpl::GetInstance()-> - GetTerminationStatus(process_.Handle(), known_dead, &exit_code_); + termination_status_ = zygote_->GetTerminationStatus( + process_.Handle(), known_dead, &exit_code_); } else if (known_dead) { termination_status_ = base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); @@ -434,9 +495,10 @@ void ChildProcessLauncher::SetProcessBackgrounded(bool background) { void ChildProcessLauncher::DidLaunch( base::WeakPtr instance, bool terminate_on_shutdown, - bool zygote, + ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, + base::ScopedFD mojo_fd, #endif base::Process process) { if (!process.IsValid()) @@ -459,16 +521,21 @@ void ChildProcessLauncher::DidLaunch( } } -void ChildProcessLauncher::Notify( - bool zygote, +void ChildProcessLauncher::Notify(ZygoteHandle zygote, #if defined(OS_ANDROID) - base::ScopedFD ipcfd, + base::ScopedFD ipcfd, #endif - base::Process process) { + base::Process process) { DCHECK(CalledOnValidThread()); starting_ = false; process_ = std::move(process); + if (process_.IsValid()) { + // Set up Mojo IPC to the new process. + mojo::edk::ChildProcessLaunched(process_.Handle(), + mojo_platform_channel_.PassServerHandle()); + } + #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) zygote_ = zygote; #endif diff --git a/chromium/content/browser/child_process_launcher.h b/chromium/content/browser/child_process_launcher.h index c4f63210af1..710247778bb 100644 --- a/chromium/content/browser/child_process_launcher.h +++ b/chromium/content/browser/child_process_launcher.h @@ -15,13 +15,14 @@ #include "build/build_config.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "mojo/edk/embedder/platform_channel_pair.h" namespace base { class CommandLine; } namespace content { -class SandboxedProcessLauncherDelegate; // Launches a process asynchronously and notifies the client of the process // handle when it's available. It's used to avoid blocking the calling thread @@ -95,14 +96,15 @@ class CONTENT_EXPORT ChildProcessLauncher : public base::NonThreadSafe { // client went away. static void DidLaunch(base::WeakPtr instance, bool terminate_on_shutdown, - bool zygote, + ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, + base::ScopedFD mojo_fd, #endif base::Process process); // Notifies the client about the result of the operation. - void Notify(bool zygote, + void Notify(ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, #endif @@ -122,12 +124,15 @@ class CONTENT_EXPORT ChildProcessLauncher : public base::NonThreadSafe { base::Process process_; base::TerminationStatus termination_status_; int exit_code_; - bool zygote_; + ZygoteHandle zygote_; bool starting_; // Controls whether the child process should be terminated on browser // shutdown. Default behavior is to terminate the child. const bool terminate_child_on_shutdown_; + // Platform channel used to establish Mojo IPC. + mojo::edk::PlatformChannelPair mojo_platform_channel_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(ChildProcessLauncher); diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc index 639401fc731..bd84239b04f 100644 --- a/chromium/content/browser/child_process_security_policy_impl.cc +++ b/chromium/content/browser/child_process_security_policy_impl.cc @@ -14,7 +14,6 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "build/build_config.h" -#include "content/browser/plugin_process_host.h" #include "content/browser/site_instance_impl.h" #include "content/common/site_isolation_policy.h" #include "content/public/browser/child_process_data.h" @@ -602,7 +601,7 @@ bool ChildProcessSecurityPolicyImpl::CanRequestURL( if (base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL)) return true; // Every child process can request . - // URLs like and shouldn't be requestable by + // URLs like and shouldn't be requestable by // any child process. Also, this case covers , which should // be handled internally by the process and not kicked up to the browser. return false; @@ -818,8 +817,11 @@ bool ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin(int child_id, const GURL& gurl) { base::AutoLock lock(lock_); SecurityStateMap::iterator state = security_state_.find(child_id); - if (state == security_state_.end()) - return false; + if (state == security_state_.end()) { + // TODO(nick): Returning true instead of false here is a temporary + // workaround for https://crbug.com/600441 + return true; + } return state->second->CanAccessDataForOrigin(gurl); } diff --git a/chromium/content/browser/child_process_security_policy_unittest.cc b/chromium/content/browser/child_process_security_policy_unittest.cc index 23604683ad3..455da447dd3 100644 --- a/chromium/content/browser/child_process_security_policy_unittest.cc +++ b/chromium/content/browser/child_process_security_policy_unittest.cc @@ -203,21 +203,22 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) { EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:BlAnK"))); EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:blank"))); - EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:cache"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:hang"))); - EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:memory"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:version"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:crash"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:cache"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:hang"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:version"))); - EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:memory"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:version"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:CrASh"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("abOuT:cAChe"))); - EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("aBoUt:memory"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("aBoUt:version"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:CrASh"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("abOuT:cAChe"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("aBoUt:version"))); // Requests for about: pages should be denied. p->GrantRequestURL(kRendererID, GURL("about:crash")); diff --git a/chromium/content/browser/cocoa/system_hotkey_helper_mac.h b/chromium/content/browser/cocoa/system_hotkey_helper_mac.h index d8e0153fd63..004b2087c78 100644 --- a/chromium/content/browser/cocoa/system_hotkey_helper_mac.h +++ b/chromium/content/browser/cocoa/system_hotkey_helper_mac.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_COCOA_SYSTEM_HOTKEY_HELPER_MAC_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/memory/weak_ptr.h" diff --git a/chromium/content/browser/cocoa/system_hotkey_map.mm b/chromium/content/browser/cocoa/system_hotkey_map.mm index da04fa8b844..6ee40574420 100644 --- a/chromium/content/browser/cocoa/system_hotkey_map.mm +++ b/chromium/content/browser/cocoa/system_hotkey_map.mm @@ -4,6 +4,10 @@ #import "content/browser/cocoa/system_hotkey_map.h" +#import + +#include "base/mac/scoped_nsobject.h" + #pragma mark - NSDictionary Helper Functions namespace { @@ -75,11 +79,32 @@ bool SystemHotkeyMap::ParseDictionary(NSDictionary* dictionary) { if (!dictionary) return false; - NSDictionary* hotkey_dictionaries = + NSDictionary* user_hotkey_dictionaries = DictionaryForKey(dictionary, @"AppleSymbolicHotKeys"); - if (!hotkey_dictionaries) + if (!user_hotkey_dictionaries) return false; + // Start with a dictionary of default OS X hotkeys that are not necessarily + // listed in com.apple.symbolichotkeys.plist, but should still be handled as + // reserved. + // If the user has overridden or disabled any of these hotkeys, + // -NSMutableDictionary addEntriesFromDictionary:] will ensure that the new + // values are used. + // See https://crbug.com/145062#c8 + base::scoped_nsobject hotkey_dictionaries([@{ + // Default Window switch key binding: Command + ` + // Note: The first parameter @96 is not used by |SystemHotkeyMap|. + @"27" : @{ + @"enabled" : @YES, + @"value" : @{ + @"type" : @"standard", + @"parameters" : + @[ @96 /* unused */, @(kVK_ANSI_Grave), @(NSCommandKeyMask) ], + } + } + } mutableCopy]); + [hotkey_dictionaries addEntriesFromDictionary:user_hotkey_dictionaries]; + for (NSString* hotkey_system_effect in [hotkey_dictionaries allKeys]) { if (![hotkey_system_effect isKindOfClass:[NSString class]]) continue; diff --git a/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm b/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm index 37638d472ac..a0798bec241 100644 --- a/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm +++ b/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm @@ -134,13 +134,13 @@ TEST_F(SystemHotkeyMapTest, ParseMouse) { bool result = map.ParseDictionary(dictionary); EXPECT_TRUE(result); - // Command + ` is a common key binding. It is missing. - // TODO(erikchen): OSX uses the default value when the keybinding is missing, - // so the hotkey should still be reserved. - // http://crbug.com/383558 + // Command + ` is a common key binding. It is missing, but since OS X uses the + // default value the hotkey should still be reserved. + // https://crbug.com/383558 + // https://crbug.com/145062 unsigned short key_code = kVK_ANSI_Grave; NSUInteger modifiers = NSCommandKeyMask; - EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); + EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers)); // There is a mouse keybinding for 0x08. It should not apply to keyboard // hotkeys. diff --git a/chromium/content/browser/compositor/browser_compositor_output_surface.h b/chromium/content/browser/compositor/browser_compositor_output_surface.h index 6e5ac7796b2..b3b9a4bd7a7 100644 --- a/chromium/content/browser/compositor/browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/browser_compositor_output_surface.h @@ -36,7 +36,7 @@ class CONTENT_EXPORT BrowserCompositorOutputSurface bool BindToClient(cc::OutputSurfaceClient* client) override; cc::OverlayCandidateValidator* GetOverlayCandidateValidator() const override; - // ui::CompositorOutputSurface::Observer implementation. + // ui::CompositorVSyncManager::Observer implementation. void OnUpdateVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) override; diff --git a/chromium/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm b/chromium/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm index 2b1c3eca645..3e9886b0fc9 100644 --- a/chromium/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm +++ b/chromium/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm @@ -6,9 +6,10 @@ #include -#include "cc/output/overlay_strategy_sandwich.h" +#include "base/command_line.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" +#include "ui/base/ui_base_switches.h" namespace content { @@ -31,7 +32,11 @@ void BrowserCompositorOverlayCandidateValidatorMac::GetStrategies( } bool BrowserCompositorOverlayCandidateValidatorMac::AllowCALayerOverlays() { - if (software_mirror_active_ || ca_layers_disabled_) + static bool overlays_disabled_at_command_line = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableMacOverlays); + if (software_mirror_active_ || ca_layers_disabled_ || + overlays_disabled_at_command_line) return false; return true; } diff --git a/chromium/content/browser/compositor/browser_compositor_view_mac.h b/chromium/content/browser/compositor/browser_compositor_view_mac.h deleted file mode 100644 index c6ee6ffe345..00000000000 --- a/chromium/content/browser/compositor/browser_compositor_view_mac.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_ -#define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_ - -#include "base/macros.h" -#include "ui/accelerated_widget_mac/accelerated_widget_mac.h" -#include "ui/compositor/compositor.h" -#include "ui/compositor/compositor_observer.h" - -namespace content { - -// A ui::Compositor and a gfx::AcceleratedWidget (and helper) that it draws -// into. This structure is used to efficiently recycle these structures across -// tabs (because creating a new ui::Compositor for each tab would be expensive -// in terms of time and resources). -class BrowserCompositorMac : public ui::CompositorObserver { - public: - virtual ~BrowserCompositorMac(); - - // Create a compositor, or recycle a preexisting one. - static scoped_ptr Create(); - - // Delete a compositor, or allow it to be recycled. - static void Recycle(scoped_ptr compositor); - - // Indicate that the recyclable compositor should be destroyed, and no future - // compositors should be recycled. - static void DisableRecyclingForShutdown(); - - ui::Compositor* compositor() { return &compositor_; } - ui::AcceleratedWidgetMac* accelerated_widget_mac() { - return accelerated_widget_mac_.get(); - } - - // Suspend will prevent the compositor from producing new frames. This should - // be called to avoid creating spurious frames while changing state. - // Compositors are created as suspended. - void Suspend(); - void Unsuspend(); - - private: - BrowserCompositorMac(); - - // ui::CompositorObserver implementation: - void OnCompositingDidCommit(ui::Compositor* compositor) override; - void OnCompositingStarted(ui::Compositor* compositor, - base::TimeTicks start_time) override {} - void OnCompositingEnded(ui::Compositor* compositor) override {} - void OnCompositingAborted(ui::Compositor* compositor) override {} - void OnCompositingLockStateChanged(ui::Compositor* compositor) override {} - void OnCompositingShuttingDown(ui::Compositor* compositor) override {} - - scoped_ptr accelerated_widget_mac_; - ui::Compositor compositor_; - scoped_refptr compositor_suspended_lock_; - - DISALLOW_COPY_AND_ASSIGN(BrowserCompositorMac); -}; - -// A class to keep around whenever a BrowserCompositorMac may be created. -// While at least one instance of this class exists, a spare -// BrowserCompositorViewCocoa will be kept around to be recycled so that the -// next BrowserCompositorMac to be created will be be created quickly. -class BrowserCompositorMacPlaceholder { - public: - BrowserCompositorMacPlaceholder(); - ~BrowserCompositorMacPlaceholder(); - - private: - DISALLOW_COPY_AND_ASSIGN(BrowserCompositorMacPlaceholder); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_VIEW_MAC_H_ diff --git a/chromium/content/browser/compositor/browser_compositor_view_mac.mm b/chromium/content/browser/compositor/browser_compositor_view_mac.mm deleted file mode 100644 index 4670ed02ced..00000000000 --- a/chromium/content/browser/compositor/browser_compositor_view_mac.mm +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/compositor/browser_compositor_view_mac.h" - -#include - -#include - -#include "base/lazy_instance.h" -#include "base/trace_event/trace_event.h" -#include "content/browser/compositor/image_transport_factory.h" -#include "content/browser/gpu/gpu_data_manager_impl.h" -#include "content/public/browser/context_factory.h" -#include "gpu/config/gpu_driver_bug_workaround_type.h" -#include "ui/accelerated_widget_mac/accelerated_widget_mac.h" -#include "ui/accelerated_widget_mac/window_resize_helper_mac.h" - -//////////////////////////////////////////////////////////////////////////////// -// BrowserCompositorMac - -namespace content { - -namespace { - -// Set when no browser compositors should remain alive. -bool g_has_shut_down = false; - -// The number of placeholder objects allocated. If this reaches zero, then -// the BrowserCompositorMac being held on to for recycling, -// |g_recyclable_browser_compositor|, will be freed. -uint32_t g_placeholder_count = 0; - -// A spare BrowserCompositorMac kept around for recycling. -base::LazyInstance> - g_recyclable_browser_compositor; - -bool WidgetNeedsGLFinishWorkaround() { - return GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive( - gpu::FORCE_GL_FINISH_AFTER_COMPOSITING); -} - -} // namespace - -BrowserCompositorMac::BrowserCompositorMac() - : accelerated_widget_mac_( - new ui::AcceleratedWidgetMac(WidgetNeedsGLFinishWorkaround())), - compositor_(content::GetContextFactory(), - ui::WindowResizeHelperMac::Get()->task_runner()) { - compositor_.SetAcceleratedWidget( - accelerated_widget_mac_->accelerated_widget()); - compositor_.SetLocksWillTimeOut(false); - Suspend(); - compositor_.AddObserver(this); -} - -BrowserCompositorMac::~BrowserCompositorMac() { - compositor_.RemoveObserver(this); -} - -void BrowserCompositorMac::Suspend() { - compositor_suspended_lock_ = compositor_.GetCompositorLock(); -} - -void BrowserCompositorMac::Unsuspend() { - compositor_suspended_lock_ = nullptr; -} - -void BrowserCompositorMac::OnCompositingDidCommit( - ui::Compositor* compositor_that_did_commit) { - DCHECK_EQ(compositor_that_did_commit, compositor()); - content::ImageTransportFactory::GetInstance() - ->SetCompositorSuspendedForRecycle(compositor(), false); -} - -// static -scoped_ptr BrowserCompositorMac::Create() { - DCHECK(ui::WindowResizeHelperMac::Get()->task_runner()); - if (g_recyclable_browser_compositor.Get()) - return std::move(g_recyclable_browser_compositor.Get()); - return scoped_ptr(new BrowserCompositorMac); -} - -// static -void BrowserCompositorMac::Recycle( - scoped_ptr compositor) { - DCHECK(compositor); - content::ImageTransportFactory::GetInstance() - ->SetCompositorSuspendedForRecycle(compositor->compositor(), true); - - // It is an error to have a browser compositor continue to exist after - // shutdown. - CHECK(!g_has_shut_down); - - // Make this BrowserCompositorMac recyclable for future instances. - g_recyclable_browser_compositor.Get().swap(compositor); - - // If there are no placeholders allocated, destroy the recyclable - // BrowserCompositorMac that we just populated. - if (!g_placeholder_count) - g_recyclable_browser_compositor.Get().reset(); -} - -// static -void BrowserCompositorMac::DisableRecyclingForShutdown() { - g_has_shut_down = true; - g_recyclable_browser_compositor.Get().reset(); -} - -//////////////////////////////////////////////////////////////////////////////// -// BrowserCompositorMacPlaceholder - -BrowserCompositorMacPlaceholder::BrowserCompositorMacPlaceholder() { - g_placeholder_count += 1; -} - -BrowserCompositorMacPlaceholder::~BrowserCompositorMacPlaceholder() { - DCHECK_GT(g_placeholder_count, 0u); - g_placeholder_count -= 1; - - // If there are no placeholders allocated, destroy the recyclable - // BrowserCompositorMac. - if (!g_placeholder_count) - g_recyclable_browser_compositor.Get().reset(); -} - -} // namespace content diff --git a/chromium/content/browser/compositor/buffer_queue.cc b/chromium/content/browser/compositor/buffer_queue.cc index dbe2ee24b7c..cafb6d96a18 100644 --- a/chromium/content/browser/compositor/buffer_queue.cc +++ b/chromium/content/browser/compositor/buffer_queue.cc @@ -6,12 +6,13 @@ #include "base/containers/adapters.h" #include "build/build_config.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/image_transport_factory.h" -#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gl_helper.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" +#include "gpu/command_buffer/common/gpu_memory_buffer_support.h" #include "gpu/command_buffer/service/image_factory.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkRegion.h" @@ -20,13 +21,12 @@ namespace content { -BufferQueue::BufferQueue( - scoped_refptr context_provider, - unsigned int texture_target, - unsigned int internalformat, - GLHelper* gl_helper, - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager, - int surface_id) +BufferQueue::BufferQueue(scoped_refptr context_provider, + unsigned int texture_target, + unsigned int internalformat, + GLHelper* gl_helper, + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, + int surface_id) : context_provider_(context_provider), fbo_(0), allocated_count_(0), @@ -222,10 +222,9 @@ scoped_ptr BufferQueue::GetNextSurface() { DCHECK_LT(allocated_count_, 4U); scoped_ptr buffer( - gpu_memory_buffer_manager_->AllocateGpuMemoryBufferForScanout( - size_, gpu::ImageFactory::DefaultBufferFormatForImageFormat( - internal_format_), - surface_id_)); + gpu_memory_buffer_manager_->AllocateGpuMemoryBuffer( + size_, gpu::DefaultBufferFormatForImageFormat(internal_format_), + gfx::BufferUsage::SCANOUT, surface_id_)); if (!buffer.get()) { gl->DeleteTextures(1, &texture); DLOG(ERROR) << "Failed to allocate GPU memory buffer"; diff --git a/chromium/content/browser/compositor/buffer_queue.h b/chromium/content/browser/compositor/buffer_queue.h index 4c7d9439f9d..47b4a7dd00a 100644 --- a/chromium/content/browser/compositor/buffer_queue.h +++ b/chromium/content/browser/compositor/buffer_queue.h @@ -25,9 +25,12 @@ namespace gfx { class GpuMemoryBuffer; } +namespace gpu { +class GpuMemoryBufferManager; +} + namespace content { -class BrowserGpuMemoryBufferManager; class GLHelper; // Provides a surface that manages its own buffers, backed by GpuMemoryBuffers @@ -40,7 +43,7 @@ class CONTENT_EXPORT BufferQueue { unsigned int texture_target, unsigned int internalformat, GLHelper* gl_helper, - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager, + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, int surface_id); virtual ~BufferQueue(); @@ -112,7 +115,7 @@ class CONTENT_EXPORT BufferQueue { // may be nullptr, if they represent frames that have been destroyed. std::deque> in_flight_surfaces_; GLHelper* gl_helper_; - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager_; + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_; int surface_id_; DISALLOW_COPY_AND_ASSIGN(BufferQueue); diff --git a/chromium/content/browser/compositor/buffer_queue_unittest.cc b/chromium/content/browser/compositor/buffer_queue_unittest.cc index 323d7ae8fd2..6e1dc9f05af 100644 --- a/chromium/content/browser/compositor/buffer_queue_unittest.cc +++ b/chromium/content/browser/compositor/buffer_queue_unittest.cc @@ -11,9 +11,9 @@ #include "cc/test/test_context_provider.h" #include "cc/test/test_web_graphics_context_3d.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" -#include "content/common/gpu/client/gl_helper.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -57,10 +57,15 @@ class StubBrowserGpuMemoryBufferManager : public BrowserGpuMemoryBufferManager { void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; } - scoped_ptr AllocateGpuMemoryBufferForScanout( + scoped_ptr AllocateGpuMemoryBuffer( const gfx::Size& size, gfx::BufferFormat format, + gfx::BufferUsage usage, int32_t surface_id) override { + if (!surface_id) { + return BrowserGpuMemoryBufferManager::AllocateGpuMemoryBuffer( + size, format, usage, surface_id); + } if (allocate_succeeds_) return make_scoped_ptr(new StubGpuMemoryBufferImpl); return nullptr; diff --git a/chromium/content/browser/compositor/delegated_frame_host.cc b/chromium/content/browser/compositor/delegated_frame_host.cc deleted file mode 100644 index f381da0eadd..00000000000 --- a/chromium/content/browser/compositor/delegated_frame_host.cc +++ /dev/null @@ -1,1085 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/compositor/delegated_frame_host.h" - -#include -#include -#include -#include - -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/time/default_tick_clock.h" -#include "cc/output/compositor_frame.h" -#include "cc/output/compositor_frame_ack.h" -#include "cc/output/copy_output_request.h" -#include "cc/resources/single_release_callback.h" -#include "cc/resources/texture_mailbox.h" -#include "cc/surfaces/surface.h" -#include "cc/surfaces/surface_factory.h" -#include "cc/surfaces/surface_hittest.h" -#include "cc/surfaces/surface_manager.h" -#include "content/browser/compositor/resize_lock.h" -#include "content/browser/compositor/surface_utils.h" -#include "content/browser/gpu/compositor_util.h" -#include "content/common/gpu/client/gl_helper.h" -#include "content/public/browser/render_widget_host_view_frame_subscriber.h" -#include "content/public/common/content_switches.h" -#include "media/base/video_frame.h" -#include "media/base/video_util.h" -#include "skia/ext/image_operations.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/effects/SkLumaColorFilter.h" -#include "ui/gfx/geometry/dip_util.h" - -namespace content { - -namespace { - -void SatisfyCallback(cc::SurfaceManager* manager, - cc::SurfaceSequence sequence) { - std::vector sequences; - sequences.push_back(sequence.sequence); - manager->DidSatisfySequences(sequence.id_namespace, &sequences); -} - -void RequireCallback(cc::SurfaceManager* manager, - cc::SurfaceId id, - cc::SurfaceSequence sequence) { - cc::Surface* surface = manager->GetSurfaceForId(id); - if (!surface) { - LOG(ERROR) << "Attempting to require callback on nonexistent surface"; - return; - } - surface->AddDestructionDependency(sequence); -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// DelegatedFrameHost - -DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) - : client_(client), - compositor_(nullptr), - use_surfaces_(UseSurfacesEnabled()), - tick_clock_(new base::DefaultTickClock()), - last_output_surface_id_(0), - pending_delegated_ack_count_(0), - skipped_frames_(false), - current_scale_factor_(1.f), - can_lock_compositor_(YES_CAN_LOCK), - delegated_frame_evictor_(new DelegatedFrameEvictor(this)) { - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - factory->AddObserver(this); - id_allocator_ = factory->GetContextFactory()->CreateSurfaceIdAllocator(); -} - -void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) { - delegated_frame_evictor_->SetVisible(true); - - if (surface_id_.is_null() && !frame_provider_.get() && - !released_front_lock_.get()) { - if (compositor_) - released_front_lock_ = compositor_->GetCompositorLock(); - } - - if (compositor_) { - compositor_->SetLatencyInfo(latency_info); - } -} - -bool DelegatedFrameHost::HasSavedFrame() { - return delegated_frame_evictor_->HasFrame(); -} - -void DelegatedFrameHost::WasHidden() { - delegated_frame_evictor_->SetVisible(false); - released_front_lock_ = NULL; -} - -void DelegatedFrameHost::MaybeCreateResizeLock() { - if (!ShouldCreateResizeLock()) - return; - DCHECK(compositor_); - - bool defer_compositor_lock = - can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || - can_lock_compositor_ == NO_PENDING_COMMIT; - - if (can_lock_compositor_ == YES_CAN_LOCK) - can_lock_compositor_ = YES_DID_LOCK; - - resize_lock_ = - client_->DelegatedFrameHostCreateResizeLock(defer_compositor_lock); -} - -bool DelegatedFrameHost::ShouldCreateResizeLock() { - if (!client_->DelegatedFrameCanCreateResizeLock()) - return false; - - if (resize_lock_) - return false; - - gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); - if (desired_size == current_frame_size_in_dip_ || desired_size.IsEmpty()) - return false; - - if (!compositor_) - return false; - - return true; -} - -void DelegatedFrameHost::CopyFromCompositingSurface( - const gfx::Rect& src_subrect, - const gfx::Size& output_size, - const ReadbackRequestCallback& callback, - const SkColorType preferred_color_type) { - // Only ARGB888 and RGB565 supported as of now. - bool format_support = ((preferred_color_type == kAlpha_8_SkColorType) || - (preferred_color_type == kRGB_565_SkColorType) || - (preferred_color_type == kN32_SkColorType)); - DCHECK(format_support); - if (!CanCopyToBitmap()) { - callback.Run(SkBitmap(), content::READBACK_SURFACE_UNAVAILABLE); - return; - } - - scoped_ptr request = - cc::CopyOutputRequest::CreateRequest( - base::Bind(&DelegatedFrameHost::CopyFromCompositingSurfaceHasResult, - output_size, preferred_color_type, callback)); - if (!src_subrect.IsEmpty()) - request->set_area(src_subrect); - RequestCopyOfOutput(std::move(request)); -} - -void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( - const gfx::Rect& src_subrect, - const scoped_refptr& target, - const base::Callback& callback) { - if (!CanCopyToVideoFrame()) { - callback.Run(gfx::Rect(), false); - return; - } - - scoped_ptr request = - cc::CopyOutputRequest::CreateRequest(base::Bind( - &DelegatedFrameHost:: - CopyFromCompositingSurfaceHasResultForVideo, - AsWeakPtr(), // For caching the ReadbackYUVInterface on this class. - nullptr, - target, - callback)); - request->set_area(src_subrect); - RequestCopyOfOutput(std::move(request)); -} - -bool DelegatedFrameHost::CanCopyToBitmap() const { - return compositor_ && - client_->DelegatedFrameHostGetLayer()->has_external_content(); -} - -bool DelegatedFrameHost::CanCopyToVideoFrame() const { - return compositor_ && - client_->DelegatedFrameHostGetLayer()->has_external_content(); -} - -void DelegatedFrameHost::BeginFrameSubscription( - scoped_ptr subscriber) { - frame_subscriber_ = std::move(subscriber); -} - -void DelegatedFrameHost::EndFrameSubscription() { - idle_frame_subscriber_textures_.clear(); - frame_subscriber_.reset(); -} - -uint32_t DelegatedFrameHost::GetSurfaceIdNamespace() { - if (!use_surfaces_) - return 0; - - return id_allocator_->id_namespace(); -} - -cc::SurfaceId DelegatedFrameHost::SurfaceIdAtPoint( - const gfx::Point& point, - gfx::Point* transformed_point) { - if (surface_id_.is_null()) - return surface_id_; - cc::SurfaceHittest hittest(nullptr, GetSurfaceManager()); - gfx::Transform target_transform; - cc::SurfaceId target_surface_id = - hittest.GetTargetSurfaceAtPoint(surface_id_, point, &target_transform); - *transformed_point = point; - if (!target_surface_id.is_null()) - target_transform.TransformPoint(transformed_point); - return target_surface_id; -} - -void DelegatedFrameHost::TransformPointToLocalCoordSpace( - const gfx::Point& point, - cc::SurfaceId original_surface, - gfx::Point* transformed_point) { - *transformed_point = point; - if (surface_id_.is_null() || original_surface == surface_id_) - return; - - gfx::Transform transform; - cc::SurfaceHittest hittest(nullptr, GetSurfaceManager()); - if (hittest.GetTransformToTargetSurface(surface_id_, original_surface, - &transform) && - transform.GetInverse(&transform)) { - transform.TransformPoint(transformed_point); - } -} - -bool DelegatedFrameHost::ShouldSkipFrame(gfx::Size size_in_dip) const { - // Should skip a frame only when another frame from the renderer is guaranteed - // to replace it. Otherwise may cause hangs when the renderer is waiting for - // the completion of latency infos (such as when taking a Snapshot.) - if (can_lock_compositor_ == NO_PENDING_RENDERER_FRAME || - can_lock_compositor_ == NO_PENDING_COMMIT || - !resize_lock_.get()) - return false; - - return size_in_dip != resize_lock_->expected_size(); -} - -void DelegatedFrameHost::WasResized() { - if (client_->DelegatedFrameHostDesiredSizeInDIP() != - current_frame_size_in_dip_ && - !client_->DelegatedFrameHostIsVisible()) - EvictDelegatedFrame(); - MaybeCreateResizeLock(); -} - -gfx::Size DelegatedFrameHost::GetRequestedRendererSize() const { - if (resize_lock_) - return resize_lock_->expected_size(); - else - return client_->DelegatedFrameHostDesiredSizeInDIP(); -} - -void DelegatedFrameHost::CheckResizeLock() { - if (!resize_lock_ || - resize_lock_->expected_size() != current_frame_size_in_dip_) - return; - - // Since we got the size we were looking for, unlock the compositor. But delay - // the release of the lock until we've kicked a frame with the new texture, to - // avoid resizing the UI before we have a chance to draw a "good" frame. - resize_lock_->UnlockCompositor(); -} - -void DelegatedFrameHost::AttemptFrameSubscriberCapture( - const gfx::Rect& damage_rect) { - if (!frame_subscriber() || !CanCopyToVideoFrame()) - return; - - const base::TimeTicks now = tick_clock_->NowTicks(); - base::TimeTicks present_time; - if (vsync_interval_ <= base::TimeDelta()) { - present_time = now; - } else { - const int64_t intervals_elapsed = (now - vsync_timebase_) / vsync_interval_; - present_time = vsync_timebase_ + (intervals_elapsed + 1) * vsync_interval_; - } - - scoped_refptr frame; - RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; - if (!frame_subscriber()->ShouldCaptureFrame(damage_rect, present_time, - &frame, &callback)) - return; - - // Get a texture to re-use; else, create a new one. - scoped_refptr subscriber_texture; - if (!idle_frame_subscriber_textures_.empty()) { - subscriber_texture = idle_frame_subscriber_textures_.back(); - idle_frame_subscriber_textures_.pop_back(); - } else if (GLHelper* helper = - ImageTransportFactory::GetInstance()->GetGLHelper()) { - subscriber_texture = new OwnedMailbox(helper); - } - - scoped_ptr request = - cc::CopyOutputRequest::CreateRequest(base::Bind( - &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo, - AsWeakPtr(), - subscriber_texture, - frame, - base::Bind(callback, present_time))); - // Setting the source in this copy request asks that the layer abort any prior - // uncommitted copy requests made on behalf of the same frame subscriber. - // This will not affect any of the copy requests spawned elsewhere from - // DelegatedFrameHost (e.g., a call to CopyFromCompositingSurface() for - // screenshots) since those copy requests do not specify |frame_subscriber()| - // as a source. - request->set_source(frame_subscriber()); - if (subscriber_texture.get()) { - request->SetTextureMailbox(cc::TextureMailbox( - subscriber_texture->mailbox(), subscriber_texture->sync_token(), - subscriber_texture->target())); - } - - if (surface_factory_.get()) { - // To avoid unnecessary composites, go directly to the Surface rather than - // through RequestCopyOfOutput (which goes through the browser - // compositor). - if (!request_copy_of_output_callback_for_testing_.is_null()) - request_copy_of_output_callback_for_testing_.Run(std::move(request)); - else - surface_factory_->RequestCopyOfSurface(surface_id_, std::move(request)); - } else { - request->set_area(gfx::Rect(current_frame_size_in_dip_)); - RequestCopyOfOutput(std::move(request)); - } -} - -void DelegatedFrameHost::SwapDelegatedFrame( - uint32_t output_surface_id, - scoped_ptr frame) { - DCHECK(frame->delegated_frame_data.get()); - cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get(); - float frame_device_scale_factor = frame->metadata.device_scale_factor; - - DCHECK(!frame_data->render_pass_list.empty()); - - cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); - - gfx::Size frame_size = root_pass->output_rect.size(); - gfx::Size frame_size_in_dip = - gfx::ConvertSizeToDIP(frame_device_scale_factor, frame_size); - - gfx::Rect damage_rect = root_pass->damage_rect; - damage_rect.Intersect(gfx::Rect(frame_size)); - gfx::Rect damage_rect_in_dip = - gfx::ConvertRectToDIP(frame_device_scale_factor, damage_rect); - - if (ShouldSkipFrame(frame_size_in_dip)) { - cc::CompositorFrameAck ack; - cc::TransferableResource::ReturnResources(frame_data->resource_list, - &ack.resources); - - skipped_latency_info_list_.insert(skipped_latency_info_list_.end(), - frame->metadata.latency_info.begin(), - frame->metadata.latency_info.end()); - - client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); - skipped_frames_ = true; - return; - } - - if (skipped_frames_) { - skipped_frames_ = false; - damage_rect = gfx::Rect(frame_size); - damage_rect_in_dip = gfx::Rect(frame_size_in_dip); - - // Give the same damage rect to the compositor. - cc::RenderPass* root_pass = frame_data->render_pass_list.back().get(); - root_pass->damage_rect = damage_rect; - } - - if (output_surface_id != last_output_surface_id_) { - // Resource ids are scoped by the output surface. - // If the originating output surface doesn't match the last one, it - // indicates the renderer's output surface may have been recreated, in which - // case we should recreate the DelegatedRendererLayer, to avoid matching - // resources from the old one with resources from the new one which would - // have the same id. Changing the layer to showing painted content destroys - // the DelegatedRendererLayer. - EvictDelegatedFrame(); - - surface_factory_.reset(); - if (!surface_returned_resources_.empty()) - SendReturnedDelegatedResources(last_output_surface_id_); - - // Drop the cc::DelegatedFrameResourceCollection so that we will not return - // any resources from the old output surface with the new output surface id. - if (resource_collection_.get()) { - resource_collection_->SetClient(NULL); - - if (resource_collection_->LoseAllResources()) - SendReturnedDelegatedResources(last_output_surface_id_); - - resource_collection_ = NULL; - } - last_output_surface_id_ = output_surface_id; - } - bool immediate_ack = !compositor_; - pending_delegated_ack_count_++; - - if (frame_size.IsEmpty()) { - DCHECK(frame_data->resource_list.empty()); - EvictDelegatedFrame(); - } else { - if (use_surfaces_) { - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - cc::SurfaceManager* manager = factory->GetSurfaceManager(); - if (!surface_factory_) { - surface_factory_ = - make_scoped_ptr(new cc::SurfaceFactory(manager, this)); - } - if (surface_id_.is_null() || frame_size != current_surface_size_ || - frame_size_in_dip != current_frame_size_in_dip_) { - if (!surface_id_.is_null()) - surface_factory_->Destroy(surface_id_); - surface_id_ = id_allocator_->GenerateId(); - surface_factory_->Create(surface_id_); - // manager must outlive compositors using it. - client_->DelegatedFrameHostGetLayer()->SetShowSurface( - surface_id_, - base::Bind(&SatisfyCallback, base::Unretained(manager)), - base::Bind(&RequireCallback, base::Unretained(manager)), frame_size, - frame_device_scale_factor, frame_size_in_dip); - current_surface_size_ = frame_size; - current_scale_factor_ = frame_device_scale_factor; - } - - frame->metadata.latency_info.insert(frame->metadata.latency_info.end(), - skipped_latency_info_list_.begin(), - skipped_latency_info_list_.end()); - skipped_latency_info_list_.clear(); - - gfx::Size desired_size = client_->DelegatedFrameHostDesiredSizeInDIP(); - if (desired_size != frame_size_in_dip && !desired_size.IsEmpty()) - immediate_ack = true; - - cc::SurfaceFactory::DrawCallback ack_callback; - if (compositor_ && !immediate_ack) { - ack_callback = base::Bind(&DelegatedFrameHost::SurfaceDrawn, - AsWeakPtr(), output_surface_id); - } - surface_factory_->SubmitCompositorFrame(surface_id_, std::move(frame), - ack_callback); - } else { - if (!resource_collection_.get()) { - resource_collection_ = new cc::DelegatedFrameResourceCollection; - resource_collection_->SetClient(this); - } - // If the physical frame size changes, we need a new |frame_provider_|. If - // the physical frame size is the same, but the size in DIP changed, we - // need to adjust the scale at which the frames will be drawn, and we do - // this by making a new |frame_provider_| also to ensure the scale change - // is presented in sync with the new frame content. - if (!frame_provider_.get() || - frame_size != frame_provider_->frame_size() || - frame_size_in_dip != current_frame_size_in_dip_) { - frame_provider_ = new cc::DelegatedFrameProvider( - resource_collection_.get(), std::move(frame->delegated_frame_data)); - client_->DelegatedFrameHostGetLayer()->SetShowDelegatedContent( - frame_provider_.get(), frame_size_in_dip); - } else { - frame_provider_->SetFrameData(std::move(frame->delegated_frame_data)); - } - } - } - released_front_lock_ = NULL; - current_frame_size_in_dip_ = frame_size_in_dip; - CheckResizeLock(); - - if (!damage_rect_in_dip.IsEmpty()) - client_->DelegatedFrameHostGetLayer()->OnDelegatedFrameDamage( - damage_rect_in_dip); - - if (immediate_ack) { - SendDelegatedFrameAck(output_surface_id); - } else if (!use_surfaces_) { - std::vector::const_iterator it; - for (it = frame->metadata.latency_info.begin(); - it != frame->metadata.latency_info.end(); ++it) - compositor_->SetLatencyInfo(*it); - // If we've previously skipped any latency infos add them. - for (it = skipped_latency_info_list_.begin(); - it != skipped_latency_info_list_.end(); - ++it) - compositor_->SetLatencyInfo(*it); - skipped_latency_info_list_.clear(); - AddOnCommitCallbackAndDisableLocks( - base::Bind(&DelegatedFrameHost::SendDelegatedFrameAck, - AsWeakPtr(), output_surface_id)); - } else { - AddOnCommitCallbackAndDisableLocks(base::Closure()); - } - // With Surfaces, WillDrawSurface() will be called as the trigger to attempt - // a frame subscriber capture instead. - if (!use_surfaces_) - AttemptFrameSubscriberCapture(damage_rect); - if (frame_provider_.get() || !surface_id_.is_null()) - delegated_frame_evictor_->SwappedFrame( - client_->DelegatedFrameHostIsVisible()); - // Note: the frame may have been evicted immediately. -} - -void DelegatedFrameHost::ClearDelegatedFrame() { - if (frame_provider_.get() || !surface_id_.is_null()) - EvictDelegatedFrame(); -} - -void DelegatedFrameHost::SendDelegatedFrameAck(uint32_t output_surface_id) { - cc::CompositorFrameAck ack; - if (!surface_returned_resources_.empty()) - ack.resources.swap(surface_returned_resources_); - if (resource_collection_.get()) - resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); - client_->DelegatedFrameHostSendCompositorSwapAck(output_surface_id, ack); - DCHECK_GT(pending_delegated_ack_count_, 0); - pending_delegated_ack_count_--; -} - -void DelegatedFrameHost::SurfaceDrawn(uint32_t output_surface_id, - cc::SurfaceDrawStatus drawn) { - SendDelegatedFrameAck(output_surface_id); -} - -void DelegatedFrameHost::UnusedResourcesAreAvailable() { - if (pending_delegated_ack_count_) - return; - - SendReturnedDelegatedResources(last_output_surface_id_); -} - -void DelegatedFrameHost::SendReturnedDelegatedResources( - uint32_t output_surface_id) { - cc::CompositorFrameAck ack; - if (!surface_returned_resources_.empty()) { - ack.resources.swap(surface_returned_resources_); - } else { - DCHECK(resource_collection_.get()); - resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources); - } - DCHECK(!ack.resources.empty()); - - client_->DelegatedFrameHostSendReclaimCompositorResources(output_surface_id, - ack); -} - -void DelegatedFrameHost::ReturnResources( - const cc::ReturnedResourceArray& resources) { - if (resources.empty()) - return; - std::copy(resources.begin(), - resources.end(), - std::back_inserter(surface_returned_resources_)); - if (!pending_delegated_ack_count_) - SendReturnedDelegatedResources(last_output_surface_id_); -} - -void DelegatedFrameHost::WillDrawSurface(cc::SurfaceId id, - const gfx::Rect& damage_rect) { - if (id != surface_id_) - return; - AttemptFrameSubscriberCapture(damage_rect); -} - -void DelegatedFrameHost::SetBeginFrameSource( - cc::SurfaceId surface_id, - cc::BeginFrameSource* begin_frame_source) { - // TODO(tansell): Hook this up. -} - -void DelegatedFrameHost::EvictDelegatedFrame() { - client_->DelegatedFrameHostGetLayer()->SetShowSolidColorContent(); - frame_provider_ = NULL; - if (!surface_id_.is_null()) { - surface_factory_->Destroy(surface_id_); - surface_id_ = cc::SurfaceId(); - } - delegated_frame_evictor_->DiscardedFrame(); -} - -// static -void DelegatedFrameHost::CopyFromCompositingSurfaceHasResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result) { - if (result->IsEmpty() || result->size().IsEmpty()) { - callback.Run(SkBitmap(), content::READBACK_FAILED); - return; - } - - gfx::Size output_size_in_pixel; - if (dst_size_in_pixel.IsEmpty()) - output_size_in_pixel = result->size(); - else - output_size_in_pixel = dst_size_in_pixel; - - if (result->HasTexture()) { - // GPU-accelerated path - PrepareTextureCopyOutputResult(output_size_in_pixel, color_type, callback, - std::move(result)); - return; - } - - DCHECK(result->HasBitmap()); - // Software path - PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback, - std::move(result)); -} - -static void CopyFromCompositingSurfaceFinished( - const ReadbackRequestCallback& callback, - scoped_ptr release_callback, - scoped_ptr bitmap, - scoped_ptr bitmap_pixels_lock, - bool result) { - bitmap_pixels_lock.reset(); - - gpu::SyncToken sync_token; - if (result) { - GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); - if (gl_helper) - gl_helper->GenerateSyncToken(&sync_token); - } - const bool lost_resource = !sync_token.HasData(); - release_callback->Run(sync_token, lost_resource); - - callback.Run(*bitmap, - result ? content::READBACK_SUCCESS : content::READBACK_FAILED); -} - -// static -void DelegatedFrameHost::PrepareTextureCopyOutputResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result) { - DCHECK(result->HasTexture()); - base::ScopedClosureRunner scoped_callback_runner( - base::Bind(callback, SkBitmap(), content::READBACK_FAILED)); - - // TODO(siva.gunturi): We should be able to validate the format here using - // GLHelper::IsReadbackConfigSupported before we processs the result. - // See crbug.com/415682 and crbug.com/415131. - scoped_ptr bitmap(new SkBitmap); - if (!bitmap->tryAllocPixels(SkImageInfo::Make( - dst_size_in_pixel.width(), dst_size_in_pixel.height(), color_type, - kOpaque_SkAlphaType))) { - scoped_callback_runner.Reset(base::Bind( - callback, SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE)); - return; - } - - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return; - - scoped_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*bitmap)); - uint8_t* pixels = static_cast(bitmap->getPixels()); - - cc::TextureMailbox texture_mailbox; - scoped_ptr release_callback; - result->TakeTexture(&texture_mailbox, &release_callback); - DCHECK(texture_mailbox.IsTexture()); - - ignore_result(scoped_callback_runner.Release()); - - gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox.mailbox(), texture_mailbox.sync_token(), result->size(), - gfx::Rect(result->size()), dst_size_in_pixel, pixels, color_type, - base::Bind(&CopyFromCompositingSurfaceFinished, callback, - base::Passed(&release_callback), base::Passed(&bitmap), - base::Passed(&bitmap_pixels_lock)), - GLHelper::SCALER_QUALITY_GOOD); -} - -// static -void DelegatedFrameHost::PrepareBitmapCopyOutputResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType preferred_color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result) { - SkColorType color_type = preferred_color_type; - if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) { - // Switch back to default colortype if format not supported. - color_type = kN32_SkColorType; - } - DCHECK(result->HasBitmap()); - scoped_ptr source = result->TakeBitmap(); - DCHECK(source); - SkBitmap scaled_bitmap; - if (source->width() != dst_size_in_pixel.width() || - source->height() != dst_size_in_pixel.height()) { - scaled_bitmap = - skia::ImageOperations::Resize(*source, - skia::ImageOperations::RESIZE_BEST, - dst_size_in_pixel.width(), - dst_size_in_pixel.height()); - } else { - scaled_bitmap = *source; - } - if (color_type == kN32_SkColorType) { - DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); - callback.Run(scaled_bitmap, READBACK_SUCCESS); - return; - } - DCHECK_EQ(color_type, kAlpha_8_SkColorType); - // The software path currently always returns N32 bitmap regardless of the - // |color_type| we ask for. - DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); - // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|. - SkBitmap grayscale_bitmap; - bool success = grayscale_bitmap.tryAllocPixels( - SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height())); - if (!success) { - callback.Run(SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE); - return; - } - SkCanvas canvas(grayscale_bitmap); - SkPaint paint; - skia::RefPtr filter = - skia::AdoptRef(SkLumaColorFilter::Create()); - paint.setColorFilter(filter.get()); - canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint); - callback.Run(grayscale_bitmap, READBACK_SUCCESS); -} - -// static -void DelegatedFrameHost::ReturnSubscriberTexture( - base::WeakPtr dfh, - scoped_refptr subscriber_texture, - const gpu::SyncToken& sync_token) { - if (!subscriber_texture.get()) - return; - if (!dfh) - return; - - subscriber_texture->UpdateSyncToken(sync_token); - - if (dfh->frame_subscriber_ && subscriber_texture->texture_id()) - dfh->idle_frame_subscriber_textures_.push_back(subscriber_texture); -} - -// static -void DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo( - base::WeakPtr dfh, - const base::Callback& callback, - scoped_refptr subscriber_texture, - scoped_ptr release_callback, - bool result) { - callback.Run(result); - - gpu::SyncToken sync_token; - if (result) { - GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper(); - gl_helper->GenerateSyncToken(&sync_token); - } - if (release_callback) { - // A release callback means the texture came from the compositor, so there - // should be no |subscriber_texture|. - DCHECK(!subscriber_texture.get()); - const bool lost_resource = !sync_token.HasData(); - release_callback->Run(sync_token, lost_resource); - } - ReturnSubscriberTexture(dfh, subscriber_texture, sync_token); -} - -// static -void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( - base::WeakPtr dfh, - scoped_refptr subscriber_texture, - scoped_refptr video_frame, - const base::Callback& callback, - scoped_ptr result) { - base::ScopedClosureRunner scoped_callback_runner( - base::Bind(callback, gfx::Rect(), false)); - base::ScopedClosureRunner scoped_return_subscriber_texture( - base::Bind(&ReturnSubscriberTexture, dfh, subscriber_texture, - gpu::SyncToken())); - - if (!dfh) - return; - if (result->IsEmpty()) - return; - if (result->size().IsEmpty()) - return; - - // Compute the dest size we want after the letterboxing resize. Make the - // coordinates and sizes even because we letterbox in YUV space - // (see CopyRGBToVideoFrame). They need to be even for the UV samples to - // line up correctly. - // The video frame's visible_rect() and the result's size() are both physical - // pixels. - gfx::Rect region_in_frame = media::ComputeLetterboxRegion( - video_frame->visible_rect(), result->size()); - region_in_frame = gfx::Rect(region_in_frame.x() & ~1, - region_in_frame.y() & ~1, - region_in_frame.width() & ~1, - region_in_frame.height() & ~1); - if (region_in_frame.IsEmpty()) - return; - - if (!result->HasTexture()) { - DCHECK(result->HasBitmap()); - scoped_ptr bitmap = result->TakeBitmap(); - // Scale the bitmap to the required size, if necessary. - SkBitmap scaled_bitmap; - if (result->size() != region_in_frame.size()) { - skia::ImageOperations::ResizeMethod method = - skia::ImageOperations::RESIZE_GOOD; - scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method, - region_in_frame.width(), - region_in_frame.height()); - } else { - scaled_bitmap = *bitmap.get(); - } - - { - SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); - - media::CopyRGBToVideoFrame( - reinterpret_cast(scaled_bitmap.getPixels()), - scaled_bitmap.rowBytes(), region_in_frame, video_frame.get()); - } - ignore_result(scoped_callback_runner.Release()); - callback.Run(region_in_frame, true); - return; - } - - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return; - if (subscriber_texture.get() && !subscriber_texture->texture_id()) - return; - - cc::TextureMailbox texture_mailbox; - scoped_ptr release_callback; - result->TakeTexture(&texture_mailbox, &release_callback); - DCHECK(texture_mailbox.IsTexture()); - - gfx::Rect result_rect(result->size()); - - content::ReadbackYUVInterface* yuv_readback_pipeline = - dfh->yuv_readback_pipeline_.get(); - if (yuv_readback_pipeline == NULL || - yuv_readback_pipeline->scaler()->SrcSize() != result_rect.size() || - yuv_readback_pipeline->scaler()->SrcSubrect() != result_rect || - yuv_readback_pipeline->scaler()->DstSize() != region_in_frame.size()) { - GLHelper::ScalerQuality quality = GLHelper::SCALER_QUALITY_FAST; - std::string quality_switch = switches::kTabCaptureDownscaleQuality; - // If we're scaling up, we can use the "best" quality. - if (result_rect.size().width() < region_in_frame.size().width() && - result_rect.size().height() < region_in_frame.size().height()) - quality_switch = switches::kTabCaptureUpscaleQuality; - - std::string switch_value = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - quality_switch); - if (switch_value == "fast") - quality = GLHelper::SCALER_QUALITY_FAST; - else if (switch_value == "good") - quality = GLHelper::SCALER_QUALITY_GOOD; - else if (switch_value == "best") - quality = GLHelper::SCALER_QUALITY_BEST; - - dfh->yuv_readback_pipeline_.reset( - gl_helper->CreateReadbackPipelineYUV(quality, - result_rect.size(), - result_rect, - region_in_frame.size(), - true, - true)); - yuv_readback_pipeline = dfh->yuv_readback_pipeline_.get(); - } - - ignore_result(scoped_callback_runner.Release()); - ignore_result(scoped_return_subscriber_texture.Release()); - - base::Callback finished_callback = base::Bind( - &DelegatedFrameHost::CopyFromCompositingSurfaceFinishedForVideo, - dfh->AsWeakPtr(), base::Bind(callback, region_in_frame), - subscriber_texture, base::Passed(&release_callback)); - yuv_readback_pipeline->ReadbackYUV(texture_mailbox.mailbox(), - texture_mailbox.sync_token(), - video_frame.get(), - region_in_frame.origin(), - finished_callback); -} - -//////////////////////////////////////////////////////////////////////////////// -// DelegatedFrameHost, ui::CompositorObserver implementation: - -void DelegatedFrameHost::OnCompositingDidCommit( - ui::Compositor* compositor) { - if (can_lock_compositor_ == NO_PENDING_COMMIT) { - can_lock_compositor_ = YES_CAN_LOCK; - if (resize_lock_.get() && resize_lock_->GrabDeferredLock()) - can_lock_compositor_ = YES_DID_LOCK; - } - RunOnCommitCallbacks(); - if (resize_lock_ && - resize_lock_->expected_size() == current_frame_size_in_dip_) { - resize_lock_.reset(); - client_->DelegatedFrameHostResizeLockWasReleased(); - // We may have had a resize while we had the lock (e.g. if the lock expired, - // or if the UI still gave us some resizes), so make sure we grab a new lock - // if necessary. - MaybeCreateResizeLock(); - } -} - -void DelegatedFrameHost::OnCompositingStarted( - ui::Compositor* compositor, base::TimeTicks start_time) { - last_draw_ended_ = start_time; -} - -void DelegatedFrameHost::OnCompositingEnded( - ui::Compositor* compositor) { -} - -void DelegatedFrameHost::OnCompositingAborted(ui::Compositor* compositor) { -} - -void DelegatedFrameHost::OnCompositingLockStateChanged( - ui::Compositor* compositor) { - // A compositor lock that is part of a resize lock timed out. We - // should display a renderer frame. - if (!compositor->IsLocked() && can_lock_compositor_ == YES_DID_LOCK) { - can_lock_compositor_ = NO_PENDING_RENDERER_FRAME; - } -} - -void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) { - DCHECK_EQ(compositor, compositor_); - ResetCompositor(); - DCHECK(!compositor_); -} - -void DelegatedFrameHost::OnUpdateVSyncParameters( - base::TimeTicks timebase, - base::TimeDelta interval) { - SetVSyncParameters(timebase, interval); - if (client_->DelegatedFrameHostIsVisible()) - client_->DelegatedFrameHostUpdateVSyncParameters(timebase, interval); -} - -//////////////////////////////////////////////////////////////////////////////// -// DelegatedFrameHost, ImageTransportFactoryObserver implementation: - -void DelegatedFrameHost::OnLostResources() { - if (frame_provider_.get() || !surface_id_.is_null()) - EvictDelegatedFrame(); - idle_frame_subscriber_textures_.clear(); - yuv_readback_pipeline_.reset(); - - client_->DelegatedFrameHostOnLostCompositorResources(); -} - -//////////////////////////////////////////////////////////////////////////////// -// DelegatedFrameHost, private: - -DelegatedFrameHost::~DelegatedFrameHost() { - DCHECK(!compositor_); - ImageTransportFactory::GetInstance()->RemoveObserver(this); - - if (!surface_id_.is_null()) - surface_factory_->Destroy(surface_id_); - if (resource_collection_.get()) - resource_collection_->SetClient(NULL); - - DCHECK(!vsync_manager_.get()); -} - -void DelegatedFrameHost::RunOnCommitCallbacks() { - for (std::vector::const_iterator - it = on_compositing_did_commit_callbacks_.begin(); - it != on_compositing_did_commit_callbacks_.end(); ++it) { - it->Run(); - } - on_compositing_did_commit_callbacks_.clear(); -} - -void DelegatedFrameHost::AddOnCommitCallbackAndDisableLocks( - const base::Closure& callback) { - DCHECK(compositor_); - - can_lock_compositor_ = NO_PENDING_COMMIT; - if (!callback.is_null()) - on_compositing_did_commit_callbacks_.push_back(callback); -} - -void DelegatedFrameHost::SetCompositor(ui::Compositor* compositor) { - DCHECK(!compositor_); - if (!compositor) - return; - compositor_ = compositor; - compositor_->AddObserver(this); - DCHECK(!vsync_manager_.get()); - vsync_manager_ = compositor_->vsync_manager(); - vsync_manager_->AddObserver(this); -} - -void DelegatedFrameHost::ResetCompositor() { - if (!compositor_) - return; - RunOnCommitCallbacks(); - if (resize_lock_) { - resize_lock_.reset(); - client_->DelegatedFrameHostResizeLockWasReleased(); - } - if (compositor_->HasObserver(this)) - compositor_->RemoveObserver(this); - if (vsync_manager_.get()) { - vsync_manager_->RemoveObserver(this); - vsync_manager_ = NULL; - } - compositor_ = nullptr; -} - -void DelegatedFrameHost::SetVSyncParameters(const base::TimeTicks& timebase, - const base::TimeDelta& interval) { - vsync_timebase_ = timebase; - vsync_interval_ = interval; -} - -void DelegatedFrameHost::LockResources() { - DCHECK(frame_provider_.get() || !surface_id_.is_null()); - delegated_frame_evictor_->LockFrame(); -} - -void DelegatedFrameHost::RequestCopyOfOutput( - scoped_ptr request) { - if (!request_copy_of_output_callback_for_testing_.is_null()) - request_copy_of_output_callback_for_testing_.Run(std::move(request)); - else - client_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput( - std::move(request)); -} - -void DelegatedFrameHost::UnlockResources() { - DCHECK(frame_provider_.get() || !surface_id_.is_null()); - delegated_frame_evictor_->UnlockFrame(); -} - -//////////////////////////////////////////////////////////////////////////////// -// DelegatedFrameHost, ui::LayerOwnerDelegate implementation: - -void DelegatedFrameHost::OnLayerRecreated(ui::Layer* old_layer, - ui::Layer* new_layer) { - // The new_layer is the one that will be used by our Window, so that's the one - // that should keep our frame. old_layer will be returned to the - // RecreateLayer caller, and should have a copy. - if (frame_provider_.get()) { - new_layer->SetShowDelegatedContent(frame_provider_.get(), - current_frame_size_in_dip_); - } - if (!surface_id_.is_null()) { - ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); - cc::SurfaceManager* manager = factory->GetSurfaceManager(); - new_layer->SetShowSurface( - surface_id_, base::Bind(&SatisfyCallback, base::Unretained(manager)), - base::Bind(&RequireCallback, base::Unretained(manager)), - current_surface_size_, current_scale_factor_, - current_frame_size_in_dip_); - } -} - -} // namespace content diff --git a/chromium/content/browser/compositor/delegated_frame_host.h b/chromium/content/browser/compositor/delegated_frame_host.h deleted file mode 100644 index 21dc4e0c8f9..00000000000 --- a/chromium/content/browser/compositor/delegated_frame_host.h +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_COMPOSITOR_DELEGATED_FRAME_HOST_H_ -#define CONTENT_BROWSER_COMPOSITOR_DELEGATED_FRAME_HOST_H_ - -#include - -#include - -#include "base/gtest_prod_util.h" -#include "cc/layers/delegated_frame_provider.h" -#include "cc/layers/delegated_frame_resource_collection.h" -#include "cc/output/copy_output_result.h" -#include "cc/surfaces/surface_factory_client.h" -#include "content/browser/compositor/image_transport_factory.h" -#include "content/browser/compositor/owned_mailbox.h" -#include "content/browser/renderer_host/delegated_frame_evictor.h" -#include "content/browser/renderer_host/dip_util.h" -#include "content/browser/renderer_host/render_widget_host_impl.h" -#include "content/browser/renderer_host/render_widget_host_view_base.h" -#include "content/public/browser/render_process_host.h" -#include "ui/compositor/compositor.h" -#include "ui/compositor/compositor_observer.h" -#include "ui/compositor/compositor_vsync_manager.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_owner_delegate.h" -#include "ui/events/event.h" -#include "ui/gfx/geometry/rect_conversions.h" - -namespace base { -class TickClock; -} - -namespace cc { -class SurfaceFactory; -enum class SurfaceDrawStatus; -} - -namespace media { -class VideoFrame; -} - -namespace content { - -class DelegatedFrameHost; -class ReadbackYUVInterface; -class RenderWidgetHostViewFrameSubscriber; -class RenderWidgetHostImpl; -class ResizeLock; - -// The DelegatedFrameHostClient is the interface from the DelegatedFrameHost, -// which manages delegated frames, and the ui::Compositor being used to -// display them. -class CONTENT_EXPORT DelegatedFrameHostClient { - public: - virtual ui::Layer* DelegatedFrameHostGetLayer() const = 0; - virtual bool DelegatedFrameHostIsVisible() const = 0; - virtual gfx::Size DelegatedFrameHostDesiredSizeInDIP() const = 0; - - virtual bool DelegatedFrameCanCreateResizeLock() const = 0; - virtual scoped_ptr DelegatedFrameHostCreateResizeLock( - bool defer_compositor_lock) = 0; - virtual void DelegatedFrameHostResizeLockWasReleased() = 0; - - virtual void DelegatedFrameHostSendCompositorSwapAck( - int output_surface_id, - const cc::CompositorFrameAck& ack) = 0; - virtual void DelegatedFrameHostSendReclaimCompositorResources( - int output_surface_id, - const cc::CompositorFrameAck& ack) = 0; - virtual void DelegatedFrameHostOnLostCompositorResources() = 0; - - virtual void DelegatedFrameHostUpdateVSyncParameters( - const base::TimeTicks& timebase, - const base::TimeDelta& interval) = 0; -}; - -// The DelegatedFrameHost is used to host all of the RenderWidgetHostView state -// and functionality that is associated with delegated frames being sent from -// the RenderWidget. The DelegatedFrameHost will push these changes through to -// the ui::Compositor associated with its DelegatedFrameHostClient. -class CONTENT_EXPORT DelegatedFrameHost - : public ui::CompositorObserver, - public ui::CompositorVSyncManager::Observer, - public ui::LayerOwnerDelegate, - public ImageTransportFactoryObserver, - public DelegatedFrameEvictorClient, - public cc::DelegatedFrameResourceCollectionClient, - public cc::SurfaceFactoryClient, - public base::SupportsWeakPtr { - public: - explicit DelegatedFrameHost(DelegatedFrameHostClient* client); - ~DelegatedFrameHost() override; - - // ui::CompositorObserver implementation. - void OnCompositingDidCommit(ui::Compositor* compositor) override; - void OnCompositingStarted(ui::Compositor* compositor, - base::TimeTicks start_time) override; - void OnCompositingEnded(ui::Compositor* compositor) override; - void OnCompositingAborted(ui::Compositor* compositor) override; - void OnCompositingLockStateChanged(ui::Compositor* compositor) override; - void OnCompositingShuttingDown(ui::Compositor* compositor) override; - - // ui::CompositorVSyncManager::Observer implementation. - void OnUpdateVSyncParameters(base::TimeTicks timebase, - base::TimeDelta interval) override; - - // ui::LayerOwnerObserver implementation. - void OnLayerRecreated(ui::Layer* old_layer, ui::Layer* new_layer) override; - - // ImageTransportFactoryObserver implementation. - void OnLostResources() override; - - // DelegatedFrameEvictorClient implementation. - void EvictDelegatedFrame() override; - - // cc::DelegatedFrameProviderClient implementation. - void UnusedResourcesAreAvailable() override; - - // cc::SurfaceFactoryClient implementation. - void ReturnResources(const cc::ReturnedResourceArray& resources) override; - void WillDrawSurface(cc::SurfaceId id, const gfx::Rect& damage_rect) override; - void SetBeginFrameSource(cc::SurfaceId surface_id, - cc::BeginFrameSource* begin_frame_source) override; - - bool CanCopyToBitmap() const; - - // Public interface exposed to RenderWidgetHostView. - - void SwapDelegatedFrame(uint32_t output_surface_id, - scoped_ptr frame); - void ClearDelegatedFrame(); - void WasHidden(); - void WasShown(const ui::LatencyInfo& latency_info); - void WasResized(); - bool HasSavedFrame(); - gfx::Size GetRequestedRendererSize() const; - void SetCompositor(ui::Compositor* compositor); - void ResetCompositor(); - void SetVSyncParameters(const base::TimeTicks& timebase, - const base::TimeDelta& interval); - // Note: |src_subset| is specified in DIP dimensions while |output_size| - // expects pixels. - void CopyFromCompositingSurface(const gfx::Rect& src_subrect, - const gfx::Size& output_size, - const ReadbackRequestCallback& callback, - const SkColorType preferred_color_type); - void CopyFromCompositingSurfaceToVideoFrame( - const gfx::Rect& src_subrect, - const scoped_refptr& target, - const base::Callback& callback); - bool CanCopyToVideoFrame() const; - void BeginFrameSubscription( - scoped_ptr subscriber); - void EndFrameSubscription(); - bool HasFrameSubscriber() const { return frame_subscriber_; } - uint32_t GetSurfaceIdNamespace(); - // Returns a null SurfaceId if this DelegatedFrameHost has not yet created - // a compositor Surface. - cc::SurfaceId SurfaceIdAtPoint(const gfx::Point& point, - gfx::Point* transformed_point); - - // Given the SurfaceID of a Surface that is contained within this class' - // Surface, find the relative transform between the Surfaces and apply it - // to a point. If a Surface has not yet been created this returns the - // same point with no transform applied. - void TransformPointToLocalCoordSpace(const gfx::Point& point, - cc::SurfaceId original_surface, - gfx::Point* transformed_point); - - // Exposed for tests. - cc::DelegatedFrameProvider* FrameProviderForTesting() const { - return frame_provider_.get(); - } - cc::SurfaceId SurfaceIdForTesting() const { return surface_id_; } - void OnCompositingDidCommitForTesting(ui::Compositor* compositor) { - OnCompositingDidCommit(compositor); - } - bool ReleasedFrontLockActiveForTesting() const { - return !!released_front_lock_.get(); - } - void SetRequestCopyOfOutputCallbackForTesting( - const base::Callback)>& callback) { - request_copy_of_output_callback_for_testing_ = callback; - } - - private: - friend class DelegatedFrameHostClient; - friend class RenderWidgetHostViewAuraCopyRequestTest; - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, - SkippedDelegatedFrames); - FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, - DiscardDelegatedFramesWithLocking); - - RenderWidgetHostViewFrameSubscriber* frame_subscriber() const { - return frame_subscriber_.get(); - } - bool ShouldCreateResizeLock(); - void LockResources(); - void UnlockResources(); - void RequestCopyOfOutput(scoped_ptr request); - - bool ShouldSkipFrame(gfx::Size size_in_dip) const; - - // Lazily grab a resize lock if the aura window size doesn't match the current - // frame size, to give time to the renderer. - void MaybeCreateResizeLock(); - - // Checks if the resize lock can be released because we received an new frame. - void CheckResizeLock(); - - // Run all on compositing commit callbacks. - void RunOnCommitCallbacks(); - - // Add on compositing commit callback. - void AddOnCommitCallbackAndDisableLocks(const base::Closure& callback); - - // Called after async thumbnailer task completes. Scales and crops the result - // of the copy. - static void CopyFromCompositingSurfaceHasResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result); - static void PrepareTextureCopyOutputResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result); - static void PrepareBitmapCopyOutputResult( - const gfx::Size& dst_size_in_pixel, - const SkColorType color_type, - const ReadbackRequestCallback& callback, - scoped_ptr result); - static void CopyFromCompositingSurfaceHasResultForVideo( - base::WeakPtr rwhva, - scoped_refptr subscriber_texture, - scoped_refptr video_frame, - const base::Callback& callback, - scoped_ptr result); - static void CopyFromCompositingSurfaceFinishedForVideo( - base::WeakPtr rwhva, - const base::Callback& callback, - scoped_refptr subscriber_texture, - scoped_ptr release_callback, - bool result); - static void ReturnSubscriberTexture( - base::WeakPtr rwhva, - scoped_refptr subscriber_texture, - const gpu::SyncToken& sync_token); - - void SendDelegatedFrameAck(uint32_t output_surface_id); - void SurfaceDrawn(uint32_t output_surface_id, cc::SurfaceDrawStatus drawn); - void SendReturnedDelegatedResources(uint32_t output_surface_id); - - // Called to consult the current |frame_subscriber_|, to determine and maybe - // initiate a copy-into-video-frame request. - void AttemptFrameSubscriberCapture(const gfx::Rect& damage_rect); - - DelegatedFrameHostClient* const client_; - ui::Compositor* compositor_; - - // True if this renders into a Surface, false if it renders into a delegated - // layer. - bool use_surfaces_; - - std::vector on_compositing_did_commit_callbacks_; - - // The vsync manager we are observing for changes, if any. - scoped_refptr vsync_manager_; - - // The current VSync timebase and interval. These are zero until the first - // call to SetVSyncParameters(). - base::TimeTicks vsync_timebase_; - base::TimeDelta vsync_interval_; - - // Overridable tick clock used for testing functions using current time. - scoped_ptr tick_clock_; - - // With delegated renderer, this is the last output surface, used to - // disambiguate resources with the same id coming from different output - // surfaces. - uint32_t last_output_surface_id_; - - // The number of delegated frame acks that are pending, to delay resource - // returns until the acks are sent. - int pending_delegated_ack_count_; - - // True after a delegated frame has been skipped, until a frame is not - // skipped. - bool skipped_frames_; - std::vector skipped_latency_info_list_; - - // Holds delegated resources that have been given to a DelegatedFrameProvider, - // and gives back resources when they are no longer in use for return to the - // renderer. - scoped_refptr resource_collection_; - - // Provides delegated frame updates to the cc::DelegatedRendererLayer. - scoped_refptr frame_provider_; - - // State for rendering into a Surface. - scoped_ptr id_allocator_; - scoped_ptr surface_factory_; - cc::SurfaceId surface_id_; - gfx::Size current_surface_size_; - float current_scale_factor_; - cc::ReturnedResourceArray surface_returned_resources_; - - // This lock is the one waiting for a frame of the right size to come back - // from the renderer/GPU process. It is set from the moment the aura window - // got resized, to the moment we committed the renderer frame of the same - // size. It keeps track of the size we expect from the renderer, and locks the - // compositor, as well as the UI for a short time to give a chance to the - // renderer of producing a frame of the right size. - scoped_ptr resize_lock_; - - // Keeps track of the current frame size. - gfx::Size current_frame_size_in_dip_; - - // This lock is for waiting for a front surface to become available to draw. - scoped_refptr released_front_lock_; - - enum CanLockCompositorState { - YES_CAN_LOCK, - // We locked, so at some point we'll need to kick a frame. - YES_DID_LOCK, - // No. A lock timed out, we need to kick a new frame before locking again. - NO_PENDING_RENDERER_FRAME, - // No. We've got a frame, but it hasn't been committed. - NO_PENDING_COMMIT, - }; - CanLockCompositorState can_lock_compositor_; - - base::TimeTicks last_draw_ended_; - - // Subscriber that listens to frame presentation events. - scoped_ptr frame_subscriber_; - std::vector > idle_frame_subscriber_textures_; - - // Callback used to pass the output request to the layer or to a function - // specified by a test. - base::Callback)> - request_copy_of_output_callback_for_testing_; - - // YUV readback pipeline. - scoped_ptr - yuv_readback_pipeline_; - - scoped_ptr delegated_frame_evictor_; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_COMPOSITOR_DELEGATED_FRAME_HOST_H_ diff --git a/chromium/content/browser/compositor/gl_helper.cc b/chromium/content/browser/compositor/gl_helper.cc new file mode 100644 index 00000000000..55493e90068 --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper.cc @@ -0,0 +1,1226 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/compositor/gl_helper.h" + +#include +#include + +#include +#include + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_util.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "content/browser/compositor/gl_helper_readback_support.h" +#include "content/browser/compositor/gl_helper_scaling.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/context_support.h" +#include "gpu/command_buffer/common/mailbox.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +using gpu::gles2::GLES2Interface; + +namespace { + +class ScopedFlush { + public: + explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} + + ~ScopedFlush() { gl_->Flush(); } + + private: + gpu::gles2::GLES2Interface* gl_; + + DISALLOW_COPY_AND_ASSIGN(ScopedFlush); +}; + +// Helper class for allocating and holding an RGBA texture of a given +// size and an associated framebuffer. +class TextureFrameBufferPair { + public: + TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size) + : texture_(gl), framebuffer_(gl), size_(size) { + content::ScopedTextureBinder texture_binder(gl, texture_); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + content::ScopedFramebufferBinder framebuffer_binder( + gl, framebuffer_); + gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture_, 0); + } + + GLuint texture() const { return texture_.id(); } + GLuint framebuffer() const { return framebuffer_.id(); } + gfx::Size size() const { return size_; } + + private: + content::ScopedTexture texture_; + content::ScopedFramebuffer framebuffer_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair); +}; + +// Helper class for holding a scaler, a texture for the output of that +// scaler and an associated frame buffer. This is inteded to be used +// when the output of a scaler is to be sent to a readback. +class ScalerHolder { + public: + ScalerHolder(GLES2Interface* gl, content::GLHelper::ScalerInterface* scaler) + : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {} + + void Scale(GLuint src_texture) { + scaler_->Scale(src_texture, texture_and_framebuffer_.texture()); + } + + content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); } + TextureFrameBufferPair* texture_and_framebuffer() { + return &texture_and_framebuffer_; + } + GLuint texture() const { return texture_and_framebuffer_.texture(); } + + private: + TextureFrameBufferPair texture_and_framebuffer_; + scoped_ptr scaler_; + + DISALLOW_COPY_AND_ASSIGN(ScalerHolder); +}; + +} // namespace + +namespace content { +typedef GLHelperReadbackSupport::FormatSupport FormatSupport; + +// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates +// the data needed for it. +class GLHelper::CopyTextureToImpl + : public base::SupportsWeakPtr { + public: + CopyTextureToImpl(GLES2Interface* gl, + gpu::ContextSupport* context_support, + GLHelper* helper) + : gl_(gl), + context_support_(context_support), + helper_(helper), + flush_(gl), + max_draw_buffers_(0) { + const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS); + if (!extensions) + return; + std::string extensions_string = + " " + std::string(reinterpret_cast(extensions)) + " "; + if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) { + gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_); + } + } + ~CopyTextureToImpl() { CancelRequests(); } + + GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token) { + return helper_->ConsumeMailboxToTexture(mailbox, sync_token); + } + + void CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality); + + void ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format); + + void ReadbackTextureAsync(GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback& callback); + + // Reads back bytes from the currently bound frame buffer. + // Note that dst_size is specified in bytes, not pixels. + void ReadbackAsync( + const gfx::Size& dst_size, + int32_t bytes_per_row, // generally dst_size.width() * 4 + int32_t row_stride_bytes, // generally dst_size.width() * 4 + unsigned char* out, + GLenum format, + GLenum type, + size_t bytes_per_pixel, + const base::Callback& callback); + + void ReadbackPlane(TextureFrameBufferPair* source, + const scoped_refptr& target, + int plane, + int size_shift, + const gfx::Rect& paste_rect, + ReadbackSwizzle swizzle, + const base::Callback& callback); + + GLuint CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + GLHelper::ScalerQuality quality); + + ReadbackYUVInterface* CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt); + + // Returns the maximum number of draw buffers available, + // 0 if GL_EXT_draw_buffers is not available. + GLint MaxDrawBuffers() const { return max_draw_buffers_; } + + FormatSupport GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel); + + private: + // A single request to CropScaleReadbackAndCleanTexture. + // The main thread can cancel the request, before it's handled by the helper + // thread, by resetting the texture and pixels fields. Alternatively, the + // thread marks that it handles the request by resetting the pixels field + // (meaning it guarantees that the callback with be called). + // In either case, the callback must be called exactly once, and the texture + // must be deleted by the main thread gl. + struct Request { + Request(const gfx::Size& size_, + int32_t bytes_per_row_, + int32_t row_stride_bytes_, + unsigned char* pixels_, + const base::Callback& callback_) + : done(false), + size(size_), + bytes_per_row(bytes_per_row_), + row_stride_bytes(row_stride_bytes_), + pixels(pixels_), + callback(callback_), + buffer(0), + query(0) {} + + bool done; + bool result; + gfx::Size size; + int bytes_per_row; + int row_stride_bytes; + unsigned char* pixels; + base::Callback callback; + GLuint buffer; + GLuint query; + }; + + // We must take care to call the callbacks last, as they may + // end up destroying the gl_helper and make *this invalid. + // We stick the finished requests in a stack object that calls + // the callbacks when it goes out of scope. + class FinishRequestHelper { + public: + FinishRequestHelper() {} + ~FinishRequestHelper() { + while (!requests_.empty()) { + Request* request = requests_.front(); + requests_.pop(); + request->callback.Run(request->result); + delete request; + } + } + void Add(Request* r) { requests_.push(r); } + + private: + std::queue requests_; + DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper); + }; + + // A readback pipeline that also converts the data to YUV before + // reading it back. + class ReadbackYUVImpl : public ReadbackYUVInterface { + public: + ReadbackYUVImpl(GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle); + + void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const scoped_refptr& target, + const gfx::Point& paste_location, + const base::Callback& callback) override; + + ScalerInterface* scaler() override { return scaler_.scaler(); } + + private: + GLES2Interface* gl_; + CopyTextureToImpl* copy_impl_; + gfx::Size dst_size_; + ReadbackSwizzle swizzle_; + ScalerHolder scaler_; + ScalerHolder y_; + ScalerHolder u_; + ScalerHolder v_; + + DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl); + }; + + // A readback pipeline that also converts the data to YUV before + // reading it back. This one uses Multiple Render Targets, which + // may not be supported on all platforms. + class ReadbackYUV_MRT : public ReadbackYUVInterface { + public: + ReadbackYUV_MRT(GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle); + + void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const scoped_refptr& target, + const gfx::Point& paste_location, + const base::Callback& callback) override; + + ScalerInterface* scaler() override { return scaler_.scaler(); } + + private: + GLES2Interface* gl_; + CopyTextureToImpl* copy_impl_; + gfx::Size dst_size_; + GLHelper::ScalerQuality quality_; + ReadbackSwizzle swizzle_; + ScalerHolder scaler_; + scoped_ptr pass1_shader_; + scoped_ptr pass2_shader_; + TextureFrameBufferPair y_; + ScopedTexture uv_; + TextureFrameBufferPair u_; + TextureFrameBufferPair v_; + + DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT); + }; + + // Copies the block of pixels specified with |src_subrect| from |src_texture|, + // scales it to |dst_size|, writes it into a texture, and returns its ID. + // |src_size| is the size of |src_texture|. + GLuint ScaleTexture(GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + SkColorType color_type, + GLHelper::ScalerQuality quality); + + // Converts each four consecutive pixels of the source texture into one pixel + // in the result texture with each pixel channel representing the grayscale + // color of one of the four original pixels: + // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X1X2X3X4 + // The resulting texture is still an RGBA texture (which is ~4 times narrower + // than the original). If rendered directly, it wouldn't show anything useful, + // but the data in it can be used to construct a grayscale image. + // |encoded_texture_size| is the exact size of the resulting RGBA texture. It + // is equal to src_size.width()/4 rounded upwards. Some channels in the last + // pixel ((-src_size.width()) % 4) to be exact) are padding and don't contain + // useful data. + // If swizzle is set to true, the transformed pixels are reordered: + // R1G1B1A1 R2G2B2A2 R3G3B3A3 R4G4B4A4 -> X3X2X1X4. + GLuint EncodeTextureAsGrayscale(GLuint src_texture, + const gfx::Size& src_size, + gfx::Size* const encoded_texture_size, + bool vertically_flip_texture, + bool swizzle); + + static void nullcallback(bool success) {} + void ReadbackDone(Request* request, int bytes_per_pixel); + void FinishRequest(Request* request, + bool result, + FinishRequestHelper* helper); + void CancelRequests(); + + static const float kRGBtoYColorWeights[]; + static const float kRGBtoUColorWeights[]; + static const float kRGBtoVColorWeights[]; + static const float kRGBtoGrayscaleColorWeights[]; + + GLES2Interface* gl_; + gpu::ContextSupport* context_support_; + GLHelper* helper_; + + // A scoped flush that will ensure all resource deletions are flushed when + // this object is destroyed. Must be declared before other Scoped* fields. + ScopedFlush flush_; + + std::queue request_queue_; + GLint max_draw_buffers_; +}; + +GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle) { + InitScalerImpl(); + return scaler_impl_->CreateScaler(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle); +} + +GLuint GLHelper::CopyTextureToImpl::ScaleTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + SkColorType color_type, + GLHelper::ScalerQuality quality) { + GLuint dst_texture = 0u; + gl_->GenTextures(1, &dst_texture); + { + GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; + ScopedTextureBinder texture_binder(gl_, dst_texture); + + // Use GL_RGBA for destination/temporary texture unless we're working with + // 16-bit data + if (color_type == kRGB_565_SkColorType) { + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + } + + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, dst_size.width(), + dst_size.height(), 0, format, type, NULL); + } + scoped_ptr scaler( + helper_->CreateScaler(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle)); + scaler->Scale(src_texture, dst_texture); + return dst_texture; +} + +GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale( + GLuint src_texture, + const gfx::Size& src_size, + gfx::Size* const encoded_texture_size, + bool vertically_flip_texture, + bool swizzle) { + GLuint dst_texture = 0u; + gl_->GenTextures(1, &dst_texture); + // The size of the encoded texture. + *encoded_texture_size = + gfx::Size((src_size.width() + 3) / 4, src_size.height()); + { + ScopedTextureBinder texture_binder(gl_, dst_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, encoded_texture_size->width(), + encoded_texture_size->height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + } + + helper_->InitScalerImpl(); + scoped_ptr grayscale_scaler( + helper_->scaler_impl_.get()->CreatePlanarScaler( + src_size, + gfx::Rect(0, 0, (src_size.width() + 3) & ~3, src_size.height()), + *encoded_texture_size, vertically_flip_texture, swizzle, + kRGBtoGrayscaleColorWeights)); + grayscale_scaler->Scale(src_texture, dst_texture); + return dst_texture; +} + +void GLHelper::CopyTextureToImpl::ReadbackAsync( + const gfx::Size& dst_size, + int32_t bytes_per_row, + int32_t row_stride_bytes, + unsigned char* out, + GLenum format, + GLenum type, + size_t bytes_per_pixel, + const base::Callback& callback) { + TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync"); + Request* request = + new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback); + request_queue_.push(request); + request->buffer = 0u; + + gl_->GenBuffers(1, &request->buffer); + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); + gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, + bytes_per_pixel * dst_size.GetArea(), NULL, GL_STREAM_READ); + + request->query = 0u; + gl_->GenQueriesEXT(1, &request->query); + gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query); + gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type, + NULL); + gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); + context_support_->SignalQuery( + request->query, base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), + request, bytes_per_pixel)); +} + +void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality) { + GLenum format, type; + size_t bytes_per_pixel; + SkColorType readback_color_type = out_color_type; + // Single-component textures are not supported by all GPUs, so we implement + // kAlpha_8_SkColorType support here via a special encoding (see below) using + // a 32-bit texture to represent an 8-bit image. + // Thus we use generic 32-bit readback in this case. + if (out_color_type == kAlpha_8_SkColorType) { + readback_color_type = kRGBA_8888_SkColorType; + } + + FormatSupport supported = GetReadbackConfig(readback_color_type, true, + &format, &type, &bytes_per_pixel); + + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + callback.Run(false); + return; + } + + GLuint texture = src_texture; + + // Scale texture if needed + // Optimization: SCALER_QUALITY_FAST is just a single bilinear pass, which we + // can do just as well in EncodeTextureAsGrayscale, which we will do if + // out_color_type is kAlpha_8_SkColorType, so let's skip the scaling step + // in that case. + bool scale_texture = out_color_type != kAlpha_8_SkColorType || + quality != GLHelper::SCALER_QUALITY_FAST; + if (scale_texture) { + // Don't swizzle during the scale step for kAlpha_8_SkColorType. + // We will swizzle in the encode step below if needed. + bool scale_swizzle = out_color_type == kAlpha_8_SkColorType + ? false + : supported == GLHelperReadbackSupport::SWIZZLE; + texture = ScaleTexture(src_texture, src_size, src_subrect, dst_size, true, + scale_swizzle, out_color_type == kAlpha_8_SkColorType + ? kN32_SkColorType + : out_color_type, + quality); + DCHECK(texture); + } + + gfx::Size readback_texture_size = dst_size; + // Encode texture to grayscale if needed. + if (out_color_type == kAlpha_8_SkColorType) { + // Do the vertical flip here if we haven't already done it when we scaled + // the texture. + bool encode_as_grayscale_vertical_flip = !scale_texture; + // EncodeTextureAsGrayscale by default creates a texture which should be + // read back as RGBA, so need to swizzle if the readback format is BGRA. + bool encode_as_grayscale_swizzle = format == GL_BGRA_EXT; + GLuint tmp_texture = EncodeTextureAsGrayscale( + texture, dst_size, &readback_texture_size, + encode_as_grayscale_vertical_flip, encode_as_grayscale_swizzle); + // If the scaled texture was created - delete it + if (scale_texture) + gl_->DeleteTextures(1, &texture); + texture = tmp_texture; + DCHECK(texture); + } + + // Readback the pixels of the resulting texture + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + + int32_t bytes_per_row = out_color_type == kAlpha_8_SkColorType + ? dst_size.width() + : dst_size.width() * bytes_per_pixel; + + ReadbackAsync(readback_texture_size, bytes_per_row, bytes_per_row, out, + format, type, bytes_per_pixel, callback); + gl_->DeleteTextures(1, &texture); +} + +void GLHelper::CopyTextureToImpl::ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType color_type) { + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = + GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + return; + } + + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + gl_->ReadPixels(src_rect.x(), src_rect.y(), src_rect.width(), + src_rect.height(), format, type, out); +} + +void GLHelper::CopyTextureToImpl::ReadbackTextureAsync( + GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback& callback) { + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = + GetReadbackConfig(color_type, false, &format, &type, &bytes_per_pixel); + if (supported == GLHelperReadbackSupport::NOT_SUPPORTED) { + callback.Run(false); + return; + } + + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder framebuffer_binder(gl_, + dst_framebuffer); + ScopedTextureBinder texture_binder(gl_, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + texture, 0); + ReadbackAsync(dst_size, dst_size.width() * bytes_per_pixel, + dst_size.width() * bytes_per_pixel, out, format, type, + bytes_per_pixel, callback); +} + +GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + GLHelper::ScalerQuality quality) { + return ScaleTexture(src_texture, src_size, gfx::Rect(src_size), dst_size, + vertically_flip_texture, false, + kRGBA_8888_SkColorType, // GL_RGBA + quality); +} + +void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request, + int bytes_per_pixel) { + TRACE_EVENT0("gpu.capture", + "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete"); + finished_request->done = true; + + FinishRequestHelper finish_request_helper; + + // We process transfer requests in the order they were received, regardless + // of the order we get the callbacks in. + while (!request_queue_.empty()) { + Request* request = request_queue_.front(); + if (!request->done) { + break; + } + + bool result = false; + if (request->buffer != 0) { + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); + unsigned char* data = static_cast(gl_->MapBufferCHROMIUM( + GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); + if (data) { + result = true; + if (request->bytes_per_row == request->size.width() * bytes_per_pixel && + request->bytes_per_row == request->row_stride_bytes) { + memcpy(request->pixels, data, + request->size.GetArea() * bytes_per_pixel); + } else { + unsigned char* out = request->pixels; + for (int y = 0; y < request->size.height(); y++) { + memcpy(out, data, request->bytes_per_row); + out += request->row_stride_bytes; + data += request->size.width() * bytes_per_pixel; + } + } + gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); + } + gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); + } + FinishRequest(request, result, &finish_request_helper); + } +} + +void GLHelper::CopyTextureToImpl::FinishRequest( + Request* request, + bool result, + FinishRequestHelper* finish_request_helper) { + TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest"); + DCHECK(request_queue_.front() == request); + request_queue_.pop(); + request->result = result; + ScopedFlush flush(gl_); + if (request->query != 0) { + gl_->DeleteQueriesEXT(1, &request->query); + request->query = 0; + } + if (request->buffer != 0) { + gl_->DeleteBuffers(1, &request->buffer); + request->buffer = 0; + } + finish_request_helper->Add(request); +} + +void GLHelper::CopyTextureToImpl::CancelRequests() { + FinishRequestHelper finish_request_helper; + while (!request_queue_.empty()) { + Request* request = request_queue_.front(); + FinishRequest(request, false, &finish_request_helper); + } +} + +FormatSupport GLHelper::CopyTextureToImpl::GetReadbackConfig( + SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel) { + return helper_->readback_support_->GetReadbackConfig( + color_type, can_swizzle, format, type, bytes_per_pixel); +} + +GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support) + : gl_(gl), + context_support_(context_support), + readback_support_(new GLHelperReadbackSupport(gl)) {} + +GLHelper::~GLHelper() {} + +void GLHelper::CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality) { + InitCopyTextToImpl(); + copy_texture_to_impl_->CropScaleReadbackAndCleanTexture( + src_texture, src_size, src_subrect, dst_size, out, out_color_type, + callback, quality); +} + +void GLHelper::CropScaleReadbackAndCleanMailbox( + const gpu::Mailbox& src_mailbox, + const gpu::SyncToken& sync_token, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality) { + GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_token); + CropScaleReadbackAndCleanTexture(mailbox_texture, src_size, src_subrect, + dst_size, out, out_color_type, callback, + quality); + gl_->DeleteTextures(1, &mailbox_texture); +} + +void GLHelper::ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format) { + InitCopyTextToImpl(); + copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format); +} + +void GLHelper::ReadbackTextureAsync( + GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback& callback) { + InitCopyTextToImpl(); + copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out, + color_type, callback); +} + +GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CopyAndScaleTexture( + texture, size, size, false, GLHelper::SCALER_QUALITY_FAST); +} + +GLuint GLHelper::CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + ScalerQuality quality) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CopyAndScaleTexture( + texture, src_size, dst_size, vertically_flip_texture, quality); +} + +GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) { + GLuint shader = gl_->CreateShader(type); + GLint length = strlen(source); + gl_->ShaderSource(shader, 1, &source, &length); + gl_->CompileShader(shader); + GLint compile_status = 0; + gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + GLint log_length = 0; + gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + if (log_length) { + scoped_ptr log(new GLchar[log_length]); + GLsizei returned_log_length = 0; + gl_->GetShaderInfoLog(shader, log_length, &returned_log_length, + log.get()); + LOG(ERROR) << std::string(log.get(), returned_log_length); + } + gl_->DeleteShader(shader); + return 0; + } + return shader; +} + +void GLHelper::InitCopyTextToImpl() { + // Lazily initialize |copy_texture_to_impl_| + if (!copy_texture_to_impl_) + copy_texture_to_impl_.reset( + new CopyTextureToImpl(gl_, context_support_, this)); +} + +void GLHelper::InitScalerImpl() { + // Lazily initialize |scaler_impl_| + if (!scaler_impl_) + scaler_impl_.reset(new GLHelperScaling(gl_, this)); +} + +GLint GLHelper::MaxDrawBuffers() { + InitCopyTextToImpl(); + return copy_texture_to_impl_->MaxDrawBuffers(); +} + +void GLHelper::CopySubBufferDamage(GLenum target, + GLuint texture, + GLuint previous_texture, + const SkRegion& new_damage, + const SkRegion& old_damage) { + SkRegion region(old_damage); + if (region.op(new_damage, SkRegion::kDifference_Op)) { + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder framebuffer_binder(gl_, + dst_framebuffer); + gl_->BindTexture(target, texture); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, + previous_texture, 0); + for (SkRegion::Iterator it(region); !it.done(); it.next()) { + const SkIRect& rect = it.rect(); + gl_->CopyTexSubImage2D(target, 0, rect.x(), rect.y(), rect.x(), rect.y(), + rect.width(), rect.height()); + } + gl_->BindTexture(target, 0); + gl_->Flush(); + } +} + +GLuint GLHelper::CreateTexture() { + GLuint texture = 0u; + gl_->GenTextures(1, &texture); + content::ScopedTextureBinder texture_binder(gl_, texture); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return texture; +} + +void GLHelper::DeleteTexture(GLuint texture_id) { + gl_->DeleteTextures(1, &texture_id); +} + +void GLHelper::GenerateSyncToken(gpu::SyncToken* sync_token) { + const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM(); + gl_->ShallowFlushCHROMIUM(); + gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token->GetData()); +} + +void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) { + gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); +} + +gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture( + GLuint texture_id) { + gpu::Mailbox mailbox; + gl_->GenMailboxCHROMIUM(mailbox.name); + gl_->ProduceTextureDirectCHROMIUM(texture_id, GL_TEXTURE_2D, mailbox.name); + + gpu::SyncToken sync_token; + GenerateSyncToken(&sync_token); + + return gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D); +} + +GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token) { + if (mailbox.IsZero()) + return 0; + if (sync_token.HasData()) + WaitSyncToken(sync_token); + GLuint texture = + gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); + return texture; +} + +void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) { + content::ScopedTextureBinder texture_binder(gl_, texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); +} + +void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) { + content::ScopedTextureBinder texture_binder(gl_, texture); + gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.x(), + rect.y(), rect.width(), rect.height()); +} + +void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) { + content::ScopedTextureBinder texture_binder(gl_, texture); + gl_->CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(), + size.height(), 0); +} + +void GLHelper::Flush() { + gl_->Flush(); +} + +void GLHelper::InsertOrderingBarrier() { + gl_->OrderingBarrierCHROMIUM(); +} + +void GLHelper::CopyTextureToImpl::ReadbackPlane( + TextureFrameBufferPair* source, + const scoped_refptr& target, + int plane, + int size_shift, + const gfx::Rect& paste_rect, + ReadbackSwizzle swizzle, + const base::Callback& callback) { + gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer()); + const size_t offset = target->stride(plane) * (paste_rect.y() >> size_shift) + + (paste_rect.x() >> size_shift); + ReadbackAsync(source->size(), paste_rect.width() >> size_shift, + target->stride(plane), target->data(plane) + offset, + (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA, + GL_UNSIGNED_BYTE, 4, callback); +} + +const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = { + 0.257f, 0.504f, 0.098f, 0.0625f}; +const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = { + -0.148f, -0.291f, 0.439f, 0.5f}; +const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = { + 0.439f, -0.368f, -0.071f, 0.5f}; +const float GLHelper::CopyTextureToImpl::kRGBtoGrayscaleColorWeights[] = { + 0.213f, 0.715f, 0.072f, 0.0f}; + +// YUV readback constructors. Initiates the main scaler pipeline and +// one planar scaler for each of the Y, U and V planes. +GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl( + GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle) + : gl_(gl), + copy_impl_(copy_impl), + dst_size_(dst_size), + swizzle_(swizzle), + scaler_(gl, + scaler_impl->CreateScaler(quality, + src_size, + src_subrect, + dst_size, + flip_vertically, + false)), + y_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + false, + (swizzle == kSwizzleBGRA), + kRGBtoYColorWeights)), + u_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, + 0, + (dst_size.width() + 7) & ~7, + (dst_size.height() + 1) & ~1), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + kRGBtoUColorWeights)), + v_(gl, + scaler_impl->CreatePlanarScaler( + dst_size, + gfx::Rect(0, + 0, + (dst_size.width() + 7) & ~7, + (dst_size.height() + 1) & ~1), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + kRGBtoVColorWeights)) { + DCHECK(!(dst_size.width() & 1)); + DCHECK(!(dst_size.height() & 1)); +} + +static void CallbackKeepingVideoFrameAlive( + scoped_refptr video_frame, + const base::Callback& callback, + bool success) { + callback.Run(success); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV( + const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const scoped_refptr& target, + const gfx::Point& paste_location, + const base::Callback& callback) { + DCHECK(!(paste_location.x() & 1)); + DCHECK(!(paste_location.y() & 1)); + + GLuint mailbox_texture = + copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); + + // Scale texture to right size. + scaler_.Scale(mailbox_texture); + gl_->DeleteTextures(1, &mailbox_texture); + + // Convert the scaled texture in to Y, U and V planes. + y_.Scale(scaler_.texture()); + u_.Scale(scaler_.texture()); + v_.Scale(scaler_.texture()); + + const gfx::Rect paste_rect(paste_location, dst_size_); + if (!target->visible_rect().Contains(paste_rect)) { + LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; + callback.Run(false); + return; + } + + // Read back planes, one at a time. Keep the video frame alive while doing the + // readback. + copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(), target, + media::VideoFrame::kYPlane, 0, paste_rect, swizzle_, + base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(), target, + media::VideoFrame::kUPlane, 1, paste_rect, swizzle_, + base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane( + v_.texture_and_framebuffer(), target, media::VideoFrame::kVPlane, 1, + paste_rect, swizzle_, + base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); + gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); + media::LetterboxYUV(target.get(), paste_rect); +} + +// YUV readback constructors. Initiates the main scaler pipeline and +// one planar scaler for each of the Y, U and V planes. +GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT( + GLES2Interface* gl, + CopyTextureToImpl* copy_impl, + GLHelperScaling* scaler_impl, + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + ReadbackSwizzle swizzle) + : gl_(gl), + copy_impl_(copy_impl), + dst_size_(dst_size), + quality_(quality), + swizzle_(swizzle), + scaler_(gl, + scaler_impl->CreateScaler(quality, + src_size, + src_subrect, + dst_size, + false, + false)), + pass1_shader_(scaler_impl->CreateYuvMrtShader( + dst_size, + gfx::Rect(0, 0, (dst_size.width() + 3) & ~3, dst_size.height()), + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + flip_vertically, + (swizzle == kSwizzleBGRA), + GLHelperScaling::SHADER_YUV_MRT_PASS1)), + pass2_shader_(scaler_impl->CreateYuvMrtShader( + gfx::Size((dst_size.width() + 3) / 4, dst_size.height()), + gfx::Rect(0, 0, (dst_size.width() + 7) / 8 * 2, dst_size.height()), + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2), + false, + (swizzle == kSwizzleBGRA), + GLHelperScaling::SHADER_YUV_MRT_PASS2)), + y_(gl, gfx::Size((dst_size.width() + 3) / 4, dst_size.height())), + uv_(gl), + u_(gl, + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)), + v_(gl, + gfx::Size((dst_size.width() + 7) / 8, (dst_size.height() + 1) / 2)) { + DCHECK(!(dst_size.width() & 1)); + DCHECK(!(dst_size.height() & 1)); + + content::ScopedTextureBinder texture_binder(gl, uv_); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (dst_size.width() + 3) / 4, + dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +} + +void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV( + const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const scoped_refptr& target, + const gfx::Point& paste_location, + const base::Callback& callback) { + DCHECK(!(paste_location.x() & 1)); + DCHECK(!(paste_location.y() & 1)); + + GLuint mailbox_texture = + copy_impl_->ConsumeMailboxToTexture(mailbox, sync_token); + + GLuint texture; + if (quality_ == GLHelper::SCALER_QUALITY_FAST) { + // Optimization: SCALER_QUALITY_FAST is just a single bilinear + // pass, which pass1_shader_ can do just as well, so let's skip + // the actual scaling in that case. + texture = mailbox_texture; + } else { + // Scale texture to right size. + scaler_.Scale(mailbox_texture); + texture = scaler_.texture(); + } + + std::vector outputs(2); + // Convert the scaled texture in to Y, U and V planes. + outputs[0] = y_.texture(); + outputs[1] = uv_; + pass1_shader_->Execute(texture, outputs); + + gl_->DeleteTextures(1, &mailbox_texture); + + outputs[0] = u_.texture(); + outputs[1] = v_.texture(); + pass2_shader_->Execute(uv_, outputs); + + const gfx::Rect paste_rect(paste_location, dst_size_); + if (!target->visible_rect().Contains(paste_rect)) { + LOG(DFATAL) << "Paste rect not inside VideoFrame's visible rect!"; + callback.Run(false); + return; + } + + // Read back planes, one at a time. + copy_impl_->ReadbackPlane(&y_, target, media::VideoFrame::kYPlane, 0, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane(&u_, target, media::VideoFrame::kUPlane, 1, + paste_rect, swizzle_, base::Bind(&nullcallback)); + copy_impl_->ReadbackPlane( + &v_, target, media::VideoFrame::kVPlane, 1, paste_rect, swizzle_, + base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); + gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); + media::LetterboxYUV(target.get(), paste_rect); +} + +bool GLHelper::IsReadbackConfigSupported(SkColorType color_type) { + DCHECK(readback_support_.get()); + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport support = readback_support_->GetReadbackConfig( + color_type, false, &format, &type, &bytes_per_pixel); + + return (support == GLHelperReadbackSupport::SUPPORTED); +} + +ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt) { + helper_->InitScalerImpl(); + // Just query if the best readback configuration needs a swizzle In + // ReadbackPlane() we will choose GL_RGBA/GL_BGRA_EXT based on swizzle + GLenum format, type; + size_t bytes_per_pixel; + FormatSupport supported = GetReadbackConfig(kRGBA_8888_SkColorType, true, + &format, &type, &bytes_per_pixel); + DCHECK((format == GL_RGBA || format == GL_BGRA_EXT) && + type == GL_UNSIGNED_BYTE); + + ReadbackSwizzle swizzle = kSwizzleNone; + if (supported == GLHelperReadbackSupport::SWIZZLE) + swizzle = kSwizzleBGRA; + + if (max_draw_buffers_ >= 2 && use_mrt) { + return new ReadbackYUV_MRT(gl_, this, helper_->scaler_impl_.get(), quality, + src_size, src_subrect, dst_size, flip_vertically, + swizzle); + } + return new ReadbackYUVImpl(gl_, this, helper_->scaler_impl_.get(), quality, + src_size, src_subrect, dst_size, flip_vertically, + swizzle); +} + +ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV( + ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt) { + InitCopyTextToImpl(); + return copy_texture_to_impl_->CreateReadbackPipelineYUV( + quality, src_size, src_subrect, dst_size, flip_vertically, use_mrt); +} + +} // namespace content diff --git a/chromium/content/browser/compositor/gl_helper.h b/chromium/content/browser/compositor/gl_helper.h new file mode 100644 index 00000000000..ebfc1a33c0b --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper.h @@ -0,0 +1,379 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_COMPOSITOR_GL_HELPER_H_ +#define CONTENT_BROWSER_COMPOSITOR_GL_HELPER_H_ + +#include "base/atomicops.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/common/mailbox_holder.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace gfx { +class Point; +class Rect; +class Size; +} + +namespace gpu { +class ContextSupport; +struct Mailbox; +} + +namespace media { +class VideoFrame; +}; + +class SkRegion; + +namespace content { + +class GLHelperScaling; + +class ScopedGLuint { + public: + typedef void (gpu::gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids); + typedef void (gpu::gles2::GLES2Interface::*DeleteFunc)(GLsizei n, + const GLuint* ids); + ScopedGLuint(gpu::gles2::GLES2Interface* gl, + GenFunc gen_func, + DeleteFunc delete_func) + : gl_(gl), id_(0u), delete_func_(delete_func) { + (gl_->*gen_func)(1, &id_); + } + + operator GLuint() const { return id_; } + + GLuint id() const { return id_; } + + ~ScopedGLuint() { + if (id_ != 0) { + (gl_->*delete_func_)(1, &id_); + } + } + + private: + gpu::gles2::GLES2Interface* gl_; + GLuint id_; + DeleteFunc delete_func_; + + DISALLOW_COPY_AND_ASSIGN(ScopedGLuint); +}; + +class ScopedBuffer : public ScopedGLuint { + public: + explicit ScopedBuffer(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenBuffers, + &gpu::gles2::GLES2Interface::DeleteBuffers) {} +}; + +class ScopedFramebuffer : public ScopedGLuint { + public: + explicit ScopedFramebuffer(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenFramebuffers, + &gpu::gles2::GLES2Interface::DeleteFramebuffers) {} +}; + +class ScopedTexture : public ScopedGLuint { + public: + explicit ScopedTexture(gpu::gles2::GLES2Interface* gl) + : ScopedGLuint(gl, + &gpu::gles2::GLES2Interface::GenTextures, + &gpu::gles2::GLES2Interface::DeleteTextures) {} +}; + +template +class ScopedBinder { + public: + typedef void (gpu::gles2::GLES2Interface::*BindFunc)(GLenum target, + GLuint id); + ScopedBinder(gpu::gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func) + : gl_(gl), bind_func_(bind_func) { + (gl_->*bind_func_)(Target, id); + } + + virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); } + + private: + gpu::gles2::GLES2Interface* gl_; + BindFunc bind_func_; + + DISALLOW_COPY_AND_ASSIGN(ScopedBinder); +}; + +template +class ScopedBufferBinder : ScopedBinder { + public: + ScopedBufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder(gl, id, &gpu::gles2::GLES2Interface::BindBuffer) {} +}; + +template +class ScopedFramebufferBinder : ScopedBinder { + public: + ScopedFramebufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder(gl, + id, + &gpu::gles2::GLES2Interface::BindFramebuffer) {} +}; + +template +class ScopedTextureBinder : ScopedBinder { + public: + ScopedTextureBinder(gpu::gles2::GLES2Interface* gl, GLuint id) + : ScopedBinder(gl, id, &gpu::gles2::GLES2Interface::BindTexture) { + } +}; + +class ReadbackYUVInterface; +class GLHelperReadbackSupport; + +// Provides higher level operations on top of the gpu::gles2::GLES2Interface +// interfaces. +class CONTENT_EXPORT GLHelper { + public: + GLHelper(gpu::gles2::GLES2Interface* gl, + gpu::ContextSupport* context_support); + ~GLHelper(); + + enum ScalerQuality { + // Bilinear single pass, fastest possible. + SCALER_QUALITY_FAST = 1, + + // Bilinear upscale + N * 50% bilinear downscales. + // This is still fast enough for most purposes and + // Image quality is nearly as good as the BEST option. + SCALER_QUALITY_GOOD = 2, + + // Bicubic upscale + N * 50% bicubic downscales. + // Produces very good quality scaled images, but it's + // 2-8x slower than the "GOOD" quality, so it's not always + // worth it. + SCALER_QUALITY_BEST = 3, + }; + + // Copies the block of pixels specified with |src_subrect| from |src_texture|, + // scales it to |dst_size|, and writes it into |out|. + // |src_size| is the size of |src_texture|. The result is in |out_color_type| + // format and is potentially flipped vertically to make it a correct image + // representation. |callback| is invoked with the copy result when the copy + // operation has completed. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + void CropScaleReadbackAndCleanTexture( + GLuint src_texture, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality); + + // Copies the block of pixels specified with |src_subrect| from |src_mailbox|, + // scales it to |dst_size|, and writes it into |out|. + // |src_size| is the size of |src_mailbox|. The result is in |out_color_type| + // format and is potentially flipped vertically to make it a correct image + // representation. |callback| is invoked with the copy result when the copy + // operation has completed. + // Note that the texture bound to src_mailbox will have the min/mag filter set + // to GL_LINEAR and wrap_s/t set to CLAMP_TO_EDGE in this call. src_mailbox is + // assumed to be GL_TEXTURE_2D. + void CropScaleReadbackAndCleanMailbox( + const gpu::Mailbox& src_mailbox, + const gpu::SyncToken& sync_token, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + unsigned char* out, + const SkColorType out_color_type, + const base::Callback& callback, + GLHelper::ScalerQuality quality); + + // Copies the texture data out of |texture| into |out|. |size| is the + // size of the texture. No post processing is applied to the pixels. The + // texture is assumed to have a format of GL_RGBA with a pixel type of + // GL_UNSIGNED_BYTE. This is a blocking call that calls glReadPixels on the + // current OpenGL context. + void ReadbackTextureSync(GLuint texture, + const gfx::Rect& src_rect, + unsigned char* out, + SkColorType format); + + void ReadbackTextureAsync(GLuint texture, + const gfx::Size& dst_size, + unsigned char* out, + SkColorType color_type, + const base::Callback& callback); + + // Creates a copy of the specified texture. |size| is the size of the texture. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + GLuint CopyTexture(GLuint texture, const gfx::Size& size); + + // Creates a scaled copy of the specified texture. |src_size| is the size of + // the texture and |dst_size| is the size of the resulting copy. + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + GLuint CopyAndScaleTexture(GLuint texture, + const gfx::Size& src_size, + const gfx::Size& dst_size, + bool vertically_flip_texture, + ScalerQuality quality); + + // Returns the shader compiled from the source. + GLuint CompileShaderFromSource(const GLchar* source, GLenum type); + + // Copies all pixels from |previous_texture| into |texture| that are + // inside the region covered by |old_damage| but not part of |new_damage|. + void CopySubBufferDamage(GLenum target, + GLuint texture, + GLuint previous_texture, + const SkRegion& new_damage, + const SkRegion& old_damage); + + // Simply creates a texture. + GLuint CreateTexture(); + // Deletes a texture. + void DeleteTexture(GLuint texture_id); + + // Inserts a fence sync, flushes, and generates a sync token. + void GenerateSyncToken(gpu::SyncToken* sync_token); + + // Wait for the sync token before executing further GL commands. + void WaitSyncToken(const gpu::SyncToken& sync_token); + + // Creates a mailbox holder that is attached to the given texture id, with a + // sync point to wait on before using the mailbox. Returns a holder with an + // empty mailbox on failure. + // Note the texture is assumed to be GL_TEXTURE_2D. + gpu::MailboxHolder ProduceMailboxHolderFromTexture(GLuint texture_id); + + // Creates a texture and consumes a mailbox into it. Returns 0 on failure. + // Note the mailbox is assumed to be GL_TEXTURE_2D. + GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token); + + // Resizes the texture's size to |size|. + void ResizeTexture(GLuint texture, const gfx::Size& size); + + // Copies the framebuffer data given in |rect| to |texture|. + void CopyTextureSubImage(GLuint texture, const gfx::Rect& rect); + + // Copies the all framebuffer data to |texture|. |size| specifies the + // size of the framebuffer. + void CopyTextureFullImage(GLuint texture, const gfx::Size& size); + + // Flushes GL commands. + void Flush(); + + // Force commands in the current command buffer to be executed before commands + // in other command buffers from the same process (ie channel to the GPU + // process). + void InsertOrderingBarrier(); + + // A scaler will cache all intermediate textures and programs + // needed to scale from a specified size to a destination size. + // If the source or destination sizes changes, you must create + // a new scaler. + class CONTENT_EXPORT ScalerInterface { + public: + ScalerInterface() {} + virtual ~ScalerInterface() {} + + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + virtual void Scale(GLuint source_texture, GLuint dest_texture) = 0; + virtual const gfx::Size& SrcSize() = 0; + virtual const gfx::Rect& SrcSubrect() = 0; + virtual const gfx::Size& DstSize() = 0; + }; + + // Note that the quality may be adjusted down if texture + // allocations fail or hardware doesn't support the requtested + // quality. Note that ScalerQuality enum is arranged in + // numerical order for simplicity. + ScalerInterface* CreateScaler(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle); + + // Create a readback pipeline that will scale a subsection of the source + // texture, then convert it to YUV422 planar form and then read back that. + // This reduces the amount of memory read from GPU to CPU memory by a factor + // 2.6, which can be quite handy since readbacks have very limited speed + // on some platforms. All values in |dst_size| must be a multiple of two. If + // |use_mrt| is true, the pipeline will try to optimize the YUV conversion + // using the multi-render-target extension. |use_mrt| should only be set to + // false for testing. + ReadbackYUVInterface* CreateReadbackPipelineYUV(ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool flip_vertically, + bool use_mrt); + + // Returns the maximum number of draw buffers available, + // 0 if GL_EXT_draw_buffers is not available. + GLint MaxDrawBuffers(); + + // Checks whether the readbback is supported for texture with the + // matching config. This doesnt check for cross format readbacks. + bool IsReadbackConfigSupported(SkColorType texture_format); + + protected: + class CopyTextureToImpl; + + // Creates |copy_texture_to_impl_| if NULL. + void InitCopyTextToImpl(); + // Creates |scaler_impl_| if NULL. + void InitScalerImpl(); + + enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA }; + + gpu::gles2::GLES2Interface* gl_; + gpu::ContextSupport* context_support_; + scoped_ptr copy_texture_to_impl_; + scoped_ptr scaler_impl_; + scoped_ptr readback_support_; + + DISALLOW_COPY_AND_ASSIGN(GLHelper); +}; + +// Similar to a ScalerInterface, a yuv readback pipeline will +// cache a scaler and all intermediate textures and frame buffers +// needed to scale, crop, letterbox and read back a texture from +// the GPU into CPU-accessible RAM. A single readback pipeline +// can handle multiple outstanding readbacks at the same time, but +// if the source or destination sizes change, you'll need to create +// a new readback pipeline. +class CONTENT_EXPORT ReadbackYUVInterface { + public: + ReadbackYUVInterface() {} + virtual ~ReadbackYUVInterface() {} + + // Note that |target| must use YV12 format. |paste_location| specifies where + // the captured pixels that are read back will be placed in the video frame. + // The region defined by the |paste_location| and the |dst_size| specified in + // the call to CreateReadbackPipelineYUV() must be fully contained within + // |target->visible_rect()|. + virtual void ReadbackYUV(const gpu::Mailbox& mailbox, + const gpu::SyncToken& sync_token, + const scoped_refptr& target, + const gfx::Point& paste_location, + const base::Callback& callback) = 0; + virtual GLHelper::ScalerInterface* scaler() = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COMPOSITOR_GL_HELPER_H_ diff --git a/chromium/content/browser/compositor/gl_helper_benchmark.cc b/chromium/content/browser/compositor/gl_helper_benchmark.cc new file mode 100644 index 00000000000..845d82a4264 --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_benchmark.cc @@ -0,0 +1,247 @@ +// Copyright (c) 2012 The Chromium 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 looks like a unit test, but it contains benchmarks and test +// utilities intended for manual evaluation of the scalers in +// gl_helper*. These tests produce output in the form of files and printouts, +// but cannot really "fail". There is no point in making these tests part +// of any test automation run. + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "content/browser/compositor/gl_helper.h" +#include "content/browser/compositor/gl_helper_scaling.h" +#include "gpu/command_buffer/client/gl_in_process_context.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkTypes.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gl/gl_surface.h" + +namespace content { + +content::GLHelper::ScalerQuality kQualities[] = { + content::GLHelper::SCALER_QUALITY_BEST, + content::GLHelper::SCALER_QUALITY_GOOD, + content::GLHelper::SCALER_QUALITY_FAST, +}; + +const char* kQualityNames[] = { + "best", "good", "fast", +}; + +class GLHelperTest : public testing::Test { + protected: + void SetUp() override { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = 8; + attributes.depth_size = 24; + attributes.red_size = 8; + attributes.green_size = 8; + attributes.blue_size = 8; + attributes.stencil_size = 8; + attributes.samples = 4; + attributes.sample_buffers = 1; + attributes.bind_generates_resource = false; + + context_.reset(gpu::GLInProcessContext::Create( + nullptr, /* service */ + nullptr, /* surface */ + true, /* offscreen */ + gfx::kNullAcceleratedWidget, /* window */ + gfx::Size(1, 1), /* size */ + nullptr, /* share_context */ + attributes, gfx::PreferDiscreteGpu, + ::gpu::GLInProcessContextSharedMemoryLimits(), + nullptr, /* gpu_memory_buffer_manager */ + nullptr /* image_factory */)); + gl_ = context_->GetImplementation(); + gpu::ContextSupport* support = context_->GetImplementation(); + + helper_.reset(new content::GLHelper(gl_, support)); + helper_scaling_.reset(new content::GLHelperScaling(gl_, helper_.get())); + } + + void TearDown() override { + helper_scaling_.reset(NULL); + helper_.reset(NULL); + context_.reset(NULL); + } + + void LoadPngFileToSkBitmap(const base::FilePath& filename, SkBitmap* bitmap) { + std::string compressed; + base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed); + ASSERT_TRUE(compressed.size()); + ASSERT_TRUE(gfx::PNGCodec::Decode( + reinterpret_cast(compressed.data()), + compressed.size(), bitmap)); + } + + // Save the image to a png file. Used to create the initial test files. + void SaveToFile(SkBitmap* bitmap, const base::FilePath& filename) { + std::vector compressed; + ASSERT_TRUE(gfx::PNGCodec::Encode( + static_cast(bitmap->getPixels()), + gfx::PNGCodec::FORMAT_BGRA, + gfx::Size(bitmap->width(), bitmap->height()), + static_cast(bitmap->rowBytes()), true, + std::vector(), &compressed)); + ASSERT_TRUE(compressed.size()); + FILE* f = base::OpenFile(filename, "wb"); + ASSERT_TRUE(f); + ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f), + compressed.size()); + base::CloseFile(f); + } + + scoped_ptr context_; + gpu::gles2::GLES2Interface* gl_; + scoped_ptr helper_; + scoped_ptr helper_scaling_; + std::deque x_ops_, y_ops_; +}; + +TEST_F(GLHelperTest, ScaleBenchmark) { + int output_sizes[] = {1920, 1080, 1249, 720, // Output size on pixel + 256, 144}; + int input_sizes[] = {3200, 2040, 2560, 1476, // Pixel tab size + 1920, 1080, 1280, 720, 800, 480, 256, 144}; + + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t outsize = 0; outsize < arraysize(output_sizes); outsize += 2) { + for (size_t insize = 0; insize < arraysize(input_sizes); insize += 2) { + uint32_t src_texture; + gl_->GenTextures(1, &src_texture); + uint32_t dst_texture; + gl_->GenTextures(1, &dst_texture); + uint32_t framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + const gfx::Size src_size(input_sizes[insize], input_sizes[insize + 1]); + const gfx::Size dst_size(output_sizes[outsize], + output_sizes[outsize + 1]); + SkBitmap input; + input.allocN32Pixels(src_size.width(), src_size.height()); + + SkBitmap output_pixels; + output_pixels.allocN32Pixels(dst_size.width(), dst_size.height()); + + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, dst_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dst_size.width(), + dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(), + src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + input.getPixels()); + + gfx::Rect src_subrect(0, 0, src_size.width(), src_size.height()); + scoped_ptr scaler( + helper_->CreateScaler(kQualities[q], src_size, src_subrect, + dst_size, false, false)); + // Scale once beforehand before we start measuring. + scaler->Scale(src_texture, dst_texture); + gl_->Finish(); + + base::TimeTicks start_time = base::TimeTicks::Now(); + int iterations = 0; + base::TimeTicks end_time; + while (true) { + for (int i = 0; i < 50; i++) { + iterations++; + scaler->Scale(src_texture, dst_texture); + gl_->Flush(); + } + gl_->Finish(); + end_time = base::TimeTicks::Now(); + if (iterations > 2000) { + break; + } + if ((end_time - start_time).InMillisecondsF() > 1000) { + break; + } + } + gl_->DeleteTextures(1, &dst_texture); + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + + std::string name; + name = base::StringPrintf("scale_%dx%d_to_%dx%d_%s", src_size.width(), + src_size.height(), dst_size.width(), + dst_size.height(), kQualityNames[q]); + + float ms = (end_time - start_time).InMillisecondsF() / iterations; + VLOG(0) << base::StringPrintf("*RESULT gpu_scale_time: %s=%.2f ms\n", + name.c_str(), ms); + } + } + } +} + +// This is more of a test utility than a test. +// Put an PNG image called "testimage.png" in your +// current directory, then run this test. It will +// create testoutput_Q_P.png, where Q is the scaling +// mode and P is the scaling percentage taken from +// the table below. +TEST_F(GLHelperTest, DISABLED_ScaleTestImage) { + int percents[] = { + 230, 180, 150, 110, 90, 70, 50, 49, 40, 20, 10, + }; + + SkBitmap input; + LoadPngFileToSkBitmap(base::FilePath(FILE_PATH_LITERAL("testimage.png")), + &input); + + uint32_t framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + uint32_t src_texture; + gl_->GenTextures(1, &src_texture); + const gfx::Size src_size(input.width(), input.height()); + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(), + src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + input.getPixels()); + + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t p = 0; p < arraysize(percents); p++) { + const gfx::Size dst_size(input.width() * percents[p] / 100, + input.height() * percents[p] / 100); + uint32_t dst_texture = helper_->CopyAndScaleTexture( + src_texture, src_size, dst_size, false, kQualities[q]); + + SkBitmap output_pixels; + output_pixels.allocN32Pixels(dst_size.width(), dst_size.height()); + + helper_->ReadbackTextureSync( + dst_texture, gfx::Rect(0, 0, dst_size.width(), dst_size.height()), + static_cast(output_pixels.getPixels()), + kN32_SkColorType); + gl_->DeleteTextures(1, &dst_texture); + std::string filename = base::StringPrintf("testoutput_%s_%d.ppm", + kQualityNames[q], percents[p]); + VLOG(0) << "Writing " << filename; + SaveToFile(&output_pixels, base::FilePath::FromUTF8Unsafe(filename)); + } + } + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); +} + +} // namespace diff --git a/chromium/content/browser/compositor/gl_helper_readback_support.cc b/chromium/content/browser/compositor/gl_helper_readback_support.cc new file mode 100644 index 00000000000..c389eda6ba5 --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_readback_support.cc @@ -0,0 +1,172 @@ +// 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 "content/browser/compositor/gl_helper_readback_support.h" +#include "base/logging.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "third_party/skia/include/core/SkImageInfo.h" + +namespace content { + +GLHelperReadbackSupport::GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl) + : gl_(gl) { + InitializeReadbackSupport(); +} + +GLHelperReadbackSupport::~GLHelperReadbackSupport() {} + +void GLHelperReadbackSupport::InitializeReadbackSupport() { + // We are concerned about 16, 32-bit formats only. The below are the most + // used 16, 32-bit formats. In future if any new format support is needed + // that should be added here. Initialize the array with + // GLHelperReadbackSupport::NOT_SUPPORTED as we dont know the supported + // formats yet. + for (int i = 0; i <= kLastEnum_SkColorType; ++i) { + format_support_table_[i] = GLHelperReadbackSupport::NOT_SUPPORTED; + } + // TODO(sikugu): kAlpha_8_SkColorType support check is failing on mesa. + // See crbug.com/415667. + CheckForReadbackSupport(kRGB_565_SkColorType); + CheckForReadbackSupport(kARGB_4444_SkColorType); + CheckForReadbackSupport(kRGBA_8888_SkColorType); + CheckForReadbackSupport(kBGRA_8888_SkColorType); + // Further any formats, support should be checked here. +} + +void GLHelperReadbackSupport::CheckForReadbackSupport( + SkColorType texture_format) { + bool supports_format = false; + switch (texture_format) { + case kRGB_565_SkColorType: + supports_format = SupportsFormat(GL_RGB, GL_UNSIGNED_SHORT_5_6_5); + break; + case kRGBA_8888_SkColorType: + // This is the baseline, assume always true. + supports_format = true; + break; + case kBGRA_8888_SkColorType: + supports_format = SupportsFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE); + break; + case kARGB_4444_SkColorType: + supports_format = false; + break; + default: + NOTREACHED(); + supports_format = false; + break; + } + DCHECK((int)texture_format <= (int)kLastEnum_SkColorType); + format_support_table_[texture_format] = + supports_format ? GLHelperReadbackSupport::SUPPORTED + : GLHelperReadbackSupport::NOT_SUPPORTED; +} + +void GLHelperReadbackSupport::GetAdditionalFormat(GLenum format, + GLenum type, + GLenum* format_out, + GLenum* type_out) { + for (unsigned int i = 0; i < format_cache_.size(); i++) { + if (format_cache_[i].format == format && format_cache_[i].type == type) { + *format_out = format_cache_[i].read_format; + *type_out = format_cache_[i].read_type; + return; + } + } + + const int kTestSize = 64; + content::ScopedTexture dst_texture(gl_); + ScopedTextureBinder texture_binder(gl_, dst_texture); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, kTestSize, kTestSize, 0, format, + type, NULL); + ScopedFramebuffer dst_framebuffer(gl_); + ScopedFramebufferBinder framebuffer_binder(gl_, + dst_framebuffer); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + dst_texture, 0); + GLint format_tmp = 0, type_tmp = 0; + gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format_tmp); + gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type_tmp); + *format_out = format_tmp; + *type_out = type_tmp; + + struct FormatCacheEntry entry = {format, type, *format_out, *type_out}; + format_cache_.push_back(entry); +} + +bool GLHelperReadbackSupport::SupportsFormat(GLenum format, GLenum type) { + // GLES2.0 Specification says this pairing is always supported + // with additional format from GL_IMPLEMENTATION_COLOR_READ_FORMAT/TYPE + if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) + return true; + + bool supports_format = false; + GLenum ext_format = 0, ext_type = 0; + GetAdditionalFormat(format, type, &ext_format, &ext_type); + if ((ext_format == format) && (ext_type == type)) { + supports_format = true; + } + return supports_format; +} + +GLHelperReadbackSupport::FormatSupport +GLHelperReadbackSupport::GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel) { + DCHECK(format && type && bytes_per_pixel); + *bytes_per_pixel = 4; + *type = GL_UNSIGNED_BYTE; + GLenum new_format = 0, new_type = 0; + switch (color_type) { + case kRGB_565_SkColorType: + if (format_support_table_[color_type] == + GLHelperReadbackSupport::SUPPORTED) { + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + *bytes_per_pixel = 2; + return GLHelperReadbackSupport::SUPPORTED; + } + break; + case kRGBA_8888_SkColorType: + *format = GL_RGBA; + if (can_swizzle) { + // If GL_BGRA_EXT is advertised as the readback format through + // GL_IMPLEMENTATION_COLOR_READ_FORMAT then assume it is preferred by + // the implementation for performance. + GetAdditionalFormat(*format, *type, &new_format, &new_type); + + if (new_format == GL_BGRA_EXT && new_type == GL_UNSIGNED_BYTE) { + *format = GL_BGRA_EXT; + return GLHelperReadbackSupport::SWIZZLE; + } + } + return GLHelperReadbackSupport::SUPPORTED; + case kBGRA_8888_SkColorType: + *format = GL_BGRA_EXT; + if (format_support_table_[color_type] == + GLHelperReadbackSupport::SUPPORTED) + return GLHelperReadbackSupport::SUPPORTED; + + if (can_swizzle) { + *format = GL_RGBA; + return GLHelperReadbackSupport::SWIZZLE; + } + + break; + case kARGB_4444_SkColorType: + return GLHelperReadbackSupport::NOT_SUPPORTED; + default: + NOTREACHED(); + break; + } + + return GLHelperReadbackSupport::NOT_SUPPORTED; +} + +} // namespace content diff --git a/chromium/content/browser/compositor/gl_helper_readback_support.h b/chromium/content/browser/compositor/gl_helper_readback_support.h new file mode 100644 index 00000000000..8f83a1fbc6b --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_readback_support.h @@ -0,0 +1,75 @@ +// 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 CONTENT_BROWSER_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_ +#define CONTENT_BROWSER_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_ + +#include + +#include + +#include "content/browser/compositor/gl_helper.h" + +namespace content { + +class CONTENT_EXPORT GLHelperReadbackSupport { + public: + enum FormatSupport { SUPPORTED, SWIZZLE, NOT_SUPPORTED }; + + GLHelperReadbackSupport(gpu::gles2::GLES2Interface* gl); + + ~GLHelperReadbackSupport(); + + // For a given color type retrieve whether readback is supported and if so + // how it should be performed. The |format|, |type| and |bytes_per_pixel| are + // the values that should be used with glReadPixels to facilitate the + // readback. If |can_swizzle| is true then this method will return SWIZZLE if + // the data needs to be swizzled before using the returned |format| otherwise + // the method will return SUPPORTED to indicate that readback is permitted of + // this color othewise NOT_SUPPORTED will be returned. This method always + // overwrites the out values irrespective of the return value. + FormatSupport GetReadbackConfig(SkColorType color_type, + bool can_swizzle, + GLenum* format, + GLenum* type, + size_t* bytes_per_pixel); + // Provides the additional readback format/type pairing for a render target + // of a given format/type pairing + void GetAdditionalFormat(GLenum format, + GLenum type, + GLenum* format_out, + GLenum* type_out); + + private: + struct FormatCacheEntry { + GLenum format; + GLenum type; + GLenum read_format; + GLenum read_type; + }; + + // This populates the format_support_table with the list of supported + // formats. + void InitializeReadbackSupport(); + + // This api is called once per format and it is done in the + // InitializeReadbackSupport. We should not use this any where + // except the InitializeReadbackSupport.Calling this at other places + // can distrub the state of normal gl operations. + void CheckForReadbackSupport(SkColorType texture_format); + + // Helper functions for checking the supported texture formats. + // Avoid using this API in between texture operations, as this does some + // teture opertions (bind, attach) internally. + bool SupportsFormat(GLenum format, GLenum type); + + FormatSupport format_support_table_[kLastEnum_SkColorType + 1]; + + gpu::gles2::GLES2Interface* gl_; + std::vector format_cache_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COMPOSITOR_GL_HELPER_READBACK_SUPPORT_H_ diff --git a/chromium/content/browser/compositor/gl_helper_scaling.cc b/chromium/content/browser/compositor/gl_helper_scaling.cc new file mode 100644 index 00000000000..0c7d9775f35 --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_scaling.cc @@ -0,0 +1,881 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/compositor/gl_helper_scaling.h" + +#include + +#include +#include +#include + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +using gpu::gles2::GLES2Interface; + +namespace content { + +GLHelperScaling::GLHelperScaling(GLES2Interface* gl, GLHelper* helper) + : gl_(gl), helper_(helper), vertex_attributes_buffer_(gl_) { + InitBuffer(); +} + +GLHelperScaling::~GLHelperScaling() {} + +// Used to keep track of a generated shader program. The program +// is passed in as text through Setup and is used by calling +// UseProgram() with the right parameters. Note that |gl_| +// and |helper_| are assumed to live longer than this program. +class ShaderProgram : public base::RefCounted { + public: + ShaderProgram(GLES2Interface* gl, GLHelper* helper) + : gl_(gl), + helper_(helper), + program_(gl_->CreateProgram()), + position_location_(-1), + texcoord_location_(-1), + src_subrect_location_(-1), + src_pixelsize_location_(-1), + dst_pixelsize_location_(-1), + scaling_vector_location_(-1), + color_weights_location_(-1) {} + + // Compile shader program. + void Setup(const GLchar* vertex_shader_text, + const GLchar* fragment_shader_text); + + // UseProgram must be called with GL_TEXTURE_2D bound to the + // source texture and GL_ARRAY_BUFFER bound to a vertex + // attribute buffer. + void UseProgram(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool scale_x, + bool flip_y, + GLfloat color_weights[4]); + + bool Initialized() const { return position_location_ != -1; } + + private: + friend class base::RefCounted; + ~ShaderProgram() { gl_->DeleteProgram(program_); } + + GLES2Interface* gl_; + GLHelper* helper_; + + // A program for copying a source texture into a destination texture. + GLuint program_; + + // The location of the position in the program. + GLint position_location_; + // The location of the texture coordinate in the program. + GLint texcoord_location_; + // The location of the source texture in the program. + GLint texture_location_; + // The location of the texture coordinate of + // the sub-rectangle in the program. + GLint src_subrect_location_; + // Location of size of source image in pixels. + GLint src_pixelsize_location_; + // Location of size of destination image in pixels. + GLint dst_pixelsize_location_; + // Location of vector for scaling direction. + GLint scaling_vector_location_; + // Location of color weights. + GLint color_weights_location_; + + DISALLOW_COPY_AND_ASSIGN(ShaderProgram); +}; + +// Implementation of a single stage in a scaler pipeline. If the pipeline has +// multiple stages, it calls Scale() on the subscaler, then further scales the +// output. Caches textures and framebuffers to avoid allocating/deleting +// them once per frame, which can be expensive on some drivers. +class ScalerImpl : public GLHelper::ScalerInterface, + public GLHelperScaling::ShaderInterface { + public: + // |gl| and |copy_impl| are expected to live longer than this object. + // |src_size| is the size of the input texture in pixels. + // |dst_size| is the size of the output texutre in pixels. + // |src_subrect| is the portion of the src to copy to the output texture. + // If |scale_x| is true, we are scaling along the X axis, otherwise Y. + // If we are scaling in both X and Y, |scale_x| is ignored. + // If |vertically_flip_texture| is true, output will be upside-down. + // If |swizzle| is true, RGBA will be transformed into BGRA. + // |color_weights| are only used together with SHADER_PLANAR to specify + // how to convert RGB colors into a single value. + ScalerImpl(GLES2Interface* gl, + GLHelperScaling* scaler_helper, + const GLHelperScaling::ScalerStage& scaler_stage, + ScalerImpl* subscaler, + const float* color_weights) + : gl_(gl), + scaler_helper_(scaler_helper), + spec_(scaler_stage), + intermediate_texture_(0), + dst_framebuffer_(gl), + subscaler_(subscaler) { + if (color_weights) { + color_weights_[0] = color_weights[0]; + color_weights_[1] = color_weights[1]; + color_weights_[2] = color_weights[2]; + color_weights_[3] = color_weights[3]; + } else { + color_weights_[0] = 0.0; + color_weights_[1] = 0.0; + color_weights_[2] = 0.0; + color_weights_[3] = 0.0; + } + shader_program_ = + scaler_helper_->GetShaderProgram(spec_.shader, spec_.swizzle); + + if (subscaler_) { + intermediate_texture_ = 0u; + gl_->GenTextures(1, &intermediate_texture_); + ScopedTextureBinder texture_binder(gl_, + intermediate_texture_); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spec_.src_size.width(), + spec_.src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, + NULL); + } + } + + ~ScalerImpl() override { + if (intermediate_texture_) { + gl_->DeleteTextures(1, &intermediate_texture_); + } + } + + // GLHelperShader::ShaderInterface implementation. + void Execute(GLuint source_texture, + const std::vector& dest_textures) override { + if (subscaler_) { + subscaler_->Scale(source_texture, intermediate_texture_); + source_texture = intermediate_texture_; + } + + ScopedFramebufferBinder framebuffer_binder( + gl_, dst_framebuffer_); + DCHECK_GT(dest_textures.size(), 0U); + scoped_ptr buffers(new GLenum[dest_textures.size()]); + for (size_t t = 0; t < dest_textures.size(); t++) { + ScopedTextureBinder texture_binder(gl_, dest_textures[t]); + gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + t, + GL_TEXTURE_2D, dest_textures[t], 0); + buffers[t] = GL_COLOR_ATTACHMENT0 + t; + } + ScopedTextureBinder texture_binder(gl_, source_texture); + + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ScopedBufferBinder buffer_binder( + gl_, scaler_helper_->vertex_attributes_buffer_); + shader_program_->UseProgram(spec_.src_size, spec_.src_subrect, + spec_.dst_size, spec_.scale_x, + spec_.vertically_flip_texture, color_weights_); + gl_->Viewport(0, 0, spec_.dst_size.width(), spec_.dst_size.height()); + + if (dest_textures.size() > 1) { + DCHECK_LE(static_cast(dest_textures.size()), + scaler_helper_->helper_->MaxDrawBuffers()); + gl_->DrawBuffersEXT(dest_textures.size(), buffers.get()); + } + // Conduct texture mapping by drawing a quad composed of two triangles. + gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (dest_textures.size() > 1) { + // Set the draw buffers back to not confuse others. + gl_->DrawBuffersEXT(1, &buffers[0]); + } + } + + // GLHelper::ScalerInterface implementation. + void Scale(GLuint source_texture, GLuint dest_texture) override { + std::vector tmp(1); + tmp[0] = dest_texture; + Execute(source_texture, tmp); + } + + const gfx::Size& SrcSize() override { + if (subscaler_) { + return subscaler_->SrcSize(); + } + return spec_.src_size; + } + const gfx::Rect& SrcSubrect() override { + if (subscaler_) { + return subscaler_->SrcSubrect(); + } + return spec_.src_subrect; + } + const gfx::Size& DstSize() override { return spec_.dst_size; } + + private: + GLES2Interface* gl_; + GLHelperScaling* scaler_helper_; + GLHelperScaling::ScalerStage spec_; + GLfloat color_weights_[4]; + GLuint intermediate_texture_; + scoped_refptr shader_program_; + ScopedFramebuffer dst_framebuffer_; + scoped_ptr subscaler_; +}; + +GLHelperScaling::ScalerStage::ScalerStage(ShaderType shader_, + gfx::Size src_size_, + gfx::Rect src_subrect_, + gfx::Size dst_size_, + bool scale_x_, + bool vertically_flip_texture_, + bool swizzle_) + : shader(shader_), + src_size(src_size_), + src_subrect(src_subrect_), + dst_size(dst_size_), + scale_x(scale_x_), + vertically_flip_texture(vertically_flip_texture_), + swizzle(swizzle_) {} + +GLHelperScaling::ScalerStage::ScalerStage(const ScalerStage& other) = default; + +// The important inputs for this function is |x_ops| and +// |y_ops|. They represent scaling operations to be done +// on an imag of size |src_size|. If |quality| is SCALER_QUALITY_BEST, +// then we will interpret these scale operations literally and we'll +// create one scaler stage for each ScaleOp. However, if |quality| +// is SCALER_QUALITY_GOOD, then we can do a whole bunch of optimizations +// by combining two or more ScaleOps in to a single scaler stage. +// Normally we process ScaleOps from |y_ops| first and |x_ops| after +// all |y_ops| are processed, but sometimes we can combine one or more +// operation from both queues essentially for free. This is the reason +// why |x_ops| and |y_ops| aren't just one single queue. +void GLHelperScaling::ConvertScalerOpsToScalerStages( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::deque* x_ops, + std::deque* y_ops, + std::vector* scaler_stages) { + while (!x_ops->empty() || !y_ops->empty()) { + gfx::Size intermediate_size = src_subrect.size(); + std::deque* current_queue = NULL; + + if (!y_ops->empty()) { + current_queue = y_ops; + } else { + current_queue = x_ops; + } + + ShaderType current_shader = SHADER_BILINEAR; + switch (current_queue->front().scale_factor) { + case 0: + if (quality == GLHelper::SCALER_QUALITY_BEST) { + current_shader = SHADER_BICUBIC_UPSCALE; + } + break; + case 2: + if (quality == GLHelper::SCALER_QUALITY_BEST) { + current_shader = SHADER_BICUBIC_HALF_1D; + } + break; + case 3: + DCHECK(quality != GLHelper::SCALER_QUALITY_BEST); + current_shader = SHADER_BILINEAR3; + break; + default: + NOTREACHED(); + } + bool scale_x = current_queue->front().scale_x; + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + + // Optimization: Sometimes we can combine 2-4 scaling operations into + // one operation. + if (quality == GLHelper::SCALER_QUALITY_GOOD) { + if (!current_queue->empty() && current_shader == SHADER_BILINEAR) { + // Combine two steps in the same dimension. + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + current_shader = SHADER_BILINEAR2; + if (!current_queue->empty()) { + // Combine three steps in the same dimension. + current_queue->front().UpdateSize(&intermediate_size); + current_queue->pop_front(); + current_shader = SHADER_BILINEAR4; + } + } + // Check if we can combine some steps in the other dimension as well. + // Since all shaders currently use GL_LINEAR, we can easily scale up + // or scale down by exactly 2x at the same time as we do another + // operation. Currently, the following mergers are supported: + // * 1 bilinear Y-pass with 1 bilinear X-pass (up or down) + // * 2 bilinear Y-passes with 2 bilinear X-passes + // * 1 bilinear Y-pass with N bilinear X-pass + // * N bilinear Y-passes with 1 bilinear X-pass (down only) + // Measurements indicate that generalizing this for 3x3 and 4x4 + // makes it slower on some platforms, such as the Pixel. + if (!scale_x && x_ops->size() > 0 && x_ops->front().scale_factor <= 2) { + int x_passes = 0; + if (current_shader == SHADER_BILINEAR2 && x_ops->size() >= 2) { + // 2y + 2x passes + x_passes = 2; + current_shader = SHADER_BILINEAR2X2; + } else if (current_shader == SHADER_BILINEAR) { + // 1y + Nx passes + scale_x = true; + switch (x_ops->size()) { + case 0: + NOTREACHED(); + case 1: + if (x_ops->front().scale_factor == 3) { + current_shader = SHADER_BILINEAR3; + } + x_passes = 1; + break; + case 2: + x_passes = 2; + current_shader = SHADER_BILINEAR2; + break; + default: + x_passes = 3; + current_shader = SHADER_BILINEAR4; + break; + } + } else if (x_ops->front().scale_factor == 2) { + // Ny + 1x-downscale + x_passes = 1; + } + + for (int i = 0; i < x_passes; i++) { + x_ops->front().UpdateSize(&intermediate_size); + x_ops->pop_front(); + } + } + } + + scaler_stages->push_back(ScalerStage(current_shader, src_size, src_subrect, + intermediate_size, scale_x, + vertically_flip_texture, swizzle)); + src_size = intermediate_size; + src_subrect = gfx::Rect(intermediate_size); + vertically_flip_texture = false; + swizzle = false; + } +} + +void GLHelperScaling::ComputeScalerStages( + GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::vector* scaler_stages) { + if (quality == GLHelper::SCALER_QUALITY_FAST || + src_subrect.size() == dst_size) { + scaler_stages->push_back(ScalerStage(SHADER_BILINEAR, src_size, src_subrect, + dst_size, false, + vertically_flip_texture, swizzle)); + return; + } + + std::deque x_ops, y_ops; + GLHelperScaling::ScaleOp::AddOps(src_subrect.width(), dst_size.width(), true, + quality == GLHelper::SCALER_QUALITY_GOOD, + &x_ops); + GLHelperScaling::ScaleOp::AddOps( + src_subrect.height(), dst_size.height(), false, + quality == GLHelper::SCALER_QUALITY_GOOD, &y_ops); + + ConvertScalerOpsToScalerStages(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle, &x_ops, + &y_ops, scaler_stages); +} + +GLHelper::ScalerInterface* GLHelperScaling::CreateScaler( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle) { + std::vector scaler_stages; + ComputeScalerStages(quality, src_size, src_subrect, dst_size, + vertically_flip_texture, swizzle, &scaler_stages); + + ScalerImpl* ret = NULL; + for (unsigned int i = 0; i < scaler_stages.size(); i++) { + ret = new ScalerImpl(gl_, this, scaler_stages[i], ret, NULL); + } + return ret; +} + +GLHelper::ScalerInterface* GLHelperScaling::CreatePlanarScaler( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + const float color_weights[4]) { + ScalerStage stage(SHADER_PLANAR, src_size, src_subrect, dst_size, true, + vertically_flip_texture, swizzle); + return new ScalerImpl(gl_, this, stage, NULL, color_weights); +} + +GLHelperScaling::ShaderInterface* GLHelperScaling::CreateYuvMrtShader( + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + ShaderType shader) { + DCHECK(shader == SHADER_YUV_MRT_PASS1 || shader == SHADER_YUV_MRT_PASS2); + ScalerStage stage(shader, src_size, src_subrect, dst_size, true, + vertically_flip_texture, swizzle); + return new ScalerImpl(gl_, this, stage, NULL, NULL); +} + +const GLfloat GLHelperScaling::kVertexAttributes[] = { + -1.0f, -1.0f, 0.0f, 0.0f, // vertex 0 + 1.0f, -1.0f, 1.0f, 0.0f, // vertex 1 + -1.0f, 1.0f, 0.0f, 1.0f, // vertex 2 + 1.0f, 1.0f, 1.0f, 1.0f, +}; // vertex 3 + +void GLHelperScaling::InitBuffer() { + ScopedBufferBinder buffer_binder(gl_, + vertex_attributes_buffer_); + gl_->BufferData(GL_ARRAY_BUFFER, sizeof(kVertexAttributes), kVertexAttributes, + GL_STATIC_DRAW); +} + +scoped_refptr GLHelperScaling::GetShaderProgram(ShaderType type, + bool swizzle) { + ShaderProgramKeyType key(type, swizzle); + scoped_refptr& cache_entry(shader_programs_[key]); + if (!cache_entry.get()) { + cache_entry = new ShaderProgram(gl_, helper_); + std::basic_string vertex_program; + std::basic_string fragment_program; + std::basic_string vertex_header; + std::basic_string fragment_directives; + std::basic_string fragment_header; + std::basic_string shared_variables; + + vertex_header.append( + "precision highp float;\n" + "attribute vec2 a_position;\n" + "attribute vec2 a_texcoord;\n" + "uniform vec4 src_subrect;\n"); + + fragment_header.append( + "precision mediump float;\n" + "uniform sampler2D s_texture;\n"); + + vertex_program.append( + " gl_Position = vec4(a_position, 0.0, 1.0);\n" + " vec2 texcoord = src_subrect.xy + a_texcoord * src_subrect.zw;\n"); + + switch (type) { + case SHADER_BILINEAR: + shared_variables.append("varying vec2 v_texcoord;\n"); + vertex_program.append(" v_texcoord = texcoord;\n"); + fragment_program.append( + " gl_FragColor = texture2D(s_texture, v_texcoord);\n"); + break; + + case SHADER_BILINEAR2: + // This is equivialent to two passes of the BILINEAR shader above. + // It can be used to scale an image down 1.0x-2.0x in either dimension, + // or exactly 4x. + shared_variables.append( + "varying vec4 v_texcoords;\n"); // 2 texcoords packed in one quad + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords.xy = texcoord + step;\n" + " v_texcoords.zw = texcoord - step;\n"); + + fragment_program.append( + " gl_FragColor = (texture2D(s_texture, v_texcoords.xy) +\n" + " texture2D(s_texture, v_texcoords.zw)) / 2.0;\n"); + break; + + case SHADER_BILINEAR3: + // This is kind of like doing 1.5 passes of the BILINEAR shader. + // It can be used to scale an image down 1.5x-3.0x, or exactly 6x. + shared_variables.append( + "varying vec4 v_texcoords1;\n" // 2 texcoords packed in one quad + "varying vec2 v_texcoords2;\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 3.0;\n" + " v_texcoords1.xy = texcoord + step;\n" + " v_texcoords1.zw = texcoord;\n" + " v_texcoords2 = texcoord - step;\n"); + fragment_program.append( + " gl_FragColor = (texture2D(s_texture, v_texcoords1.xy) +\n" + " texture2D(s_texture, v_texcoords1.zw) +\n" + " texture2D(s_texture, v_texcoords2)) / 3.0;\n"); + break; + + case SHADER_BILINEAR4: + // This is equivialent to three passes of the BILINEAR shader above, + // It can be used to scale an image down 2.0x-4.0x or exactly 8x. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 8.0;\n" + " v_texcoords[0].xy = texcoord - step * 3.0;\n" + " v_texcoords[0].zw = texcoord - step;\n" + " v_texcoords[1].xy = texcoord + step;\n" + " v_texcoords[1].zw = texcoord + step * 3.0;\n"); + fragment_program.append( + " gl_FragColor = (\n" + " texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); + break; + + case SHADER_BILINEAR2X2: + // This is equivialent to four passes of the BILINEAR shader above. + // Two in each dimension. It can be used to scale an image down + // 1.0x-2.0x in both X and Y directions. Or, it could be used to + // scale an image down by exactly 4x in both dimensions. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append("uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = src_subrect.zw / 4.0 / dst_pixelsize;\n" + " v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n" + " v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n" + " v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n" + " v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n"); + fragment_program.append( + " gl_FragColor = (\n" + " texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n"); + break; + + case SHADER_BICUBIC_HALF_1D: + // This scales down texture by exactly half in one dimension. + // directions in one pass. We use bilinear lookup to reduce + // the number of texture reads from 8 to 4 + shared_variables.append( + "const float CenterDist = 99.0 / 140.0;\n" + "const float LobeDist = 11.0 / 4.0;\n" + "const float CenterWeight = 35.0 / 64.0;\n" + "const float LobeWeight = -3.0 / 64.0;\n" + "varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 src_pixelsize;\n"); + vertex_program.append( + " vec2 step = src_subrect.zw * scaling_vector / src_pixelsize;\n" + " v_texcoords[0].xy = texcoord - LobeDist * step;\n" + " v_texcoords[0].zw = texcoord - CenterDist * step;\n" + " v_texcoords[1].xy = texcoord + CenterDist * step;\n" + " v_texcoords[1].zw = texcoord + LobeDist * step;\n"); + fragment_program.append( + " gl_FragColor = \n" + // Lobe pixels + " (texture2D(s_texture, v_texcoords[0].xy) +\n" + " texture2D(s_texture, v_texcoords[1].zw)) *\n" + " LobeWeight +\n" + // Center pixels + " (texture2D(s_texture, v_texcoords[0].zw) +\n" + " texture2D(s_texture, v_texcoords[1].xy)) *\n" + " CenterWeight;\n"); + break; + + case SHADER_BICUBIC_UPSCALE: + // When scaling up, we need 4 texture reads, but we can + // save some instructions because will know in which range of + // the bicubic function each call call to the bicubic function + // will be in. + // Also, when sampling the bicubic function like this, the sum + // is always exactly one, so we can skip normalization as well. + shared_variables.append("varying vec2 v_texcoord;\n"); + vertex_program.append(" v_texcoord = texcoord;\n"); + fragment_header.append( + "uniform vec2 src_pixelsize;\n" + "uniform vec2 scaling_vector;\n" + "const float a = -0.5;\n" + // This function is equivialent to calling the bicubic + // function with x-1, x, 1-x and 2-x + // (assuming 0 <= x < 1) + "vec4 filt4(float x) {\n" + " return vec4(x * x * x, x * x, x, 1) *\n" + " mat4( a, -2.0 * a, a, 0.0,\n" + " a + 2.0, -a - 3.0, 0.0, 1.0,\n" + " -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n" + " -a, a, 0.0, 0.0);\n" + "}\n" + "mat4 pixels_x(vec2 pos, vec2 step) {\n" + " return mat4(\n" + " texture2D(s_texture, pos - step),\n" + " texture2D(s_texture, pos),\n" + " texture2D(s_texture, pos + step),\n" + " texture2D(s_texture, pos + step * 2.0));\n" + "}\n"); + fragment_program.append( + " vec2 pixel_pos = v_texcoord * src_pixelsize - \n" + " scaling_vector / 2.0;\n" + " float frac = fract(dot(pixel_pos, scaling_vector));\n" + " vec2 base = (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n" + " vec2 step = scaling_vector / src_pixelsize;\n" + " gl_FragColor = pixels_x(base, step) * filt4(frac);\n"); + break; + + case SHADER_PLANAR: + // Converts four RGBA pixels into one pixel. Each RGBA + // pixel will be dot-multiplied with the color weights and + // then placed into a component of the output. This is used to + // convert RGBA textures into Y, U and V textures. We do this + // because single-component textures are not renderable on all + // architectures. + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords[0].xy = texcoord - step * 1.5;\n" + " v_texcoords[0].zw = texcoord - step * 0.5;\n" + " v_texcoords[1].xy = texcoord + step * 0.5;\n" + " v_texcoords[1].zw = texcoord + step * 1.5;\n"); + fragment_header.append("uniform vec4 color_weights;\n"); + fragment_program.append( + " gl_FragColor = color_weights * mat4(\n" + " vec4(texture2D(s_texture, v_texcoords[0].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[0].zw).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].xy).rgb, 1.0),\n" + " vec4(texture2D(s_texture, v_texcoords[1].zw).rgb, 1.0));\n"); + break; + + case SHADER_YUV_MRT_PASS1: + // RGB24 to YV12 in two passes; writing two 8888 targets each pass. + // + // YV12 is full-resolution luma and half-resolution blue/red chroma. + // + // (original) + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // RGBX RGBX RGBX RGBX RGBX RGBX RGBX RGBX + // | + // | (y plane) (temporary) + // | YYYY YYYY UUVV UUVV + // +--> { YYYY YYYY + UUVV UUVV } + // YYYY YYYY UUVV UUVV + // First YYYY YYYY UUVV UUVV + // pass YYYY YYYY UUVV UUVV + // YYYY YYYY UUVV UUVV + // | + // | (u plane) (v plane) + // Second | UUUU VVVV + // pass +--> { UUUU + VVVV } + // UUUU VVVV + // + shared_variables.append("varying vec4 v_texcoords[2];\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 4.0;\n" + " v_texcoords[0].xy = texcoord - step * 1.5;\n" + " v_texcoords[0].zw = texcoord - step * 0.5;\n" + " v_texcoords[1].xy = texcoord + step * 0.5;\n" + " v_texcoords[1].zw = texcoord + step * 1.5;\n"); + fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); + fragment_header.append( + "const vec3 kRGBtoY = vec3(0.257, 0.504, 0.098);\n" + "const float kYBias = 0.0625;\n" + // Divide U and V by two to compensate for averaging below. + "const vec3 kRGBtoU = vec3(-0.148, -0.291, 0.439) / 2.0;\n" + "const vec3 kRGBtoV = vec3(0.439, -0.368, -0.071) / 2.0;\n" + "const float kUVBias = 0.5;\n"); + fragment_program.append( + " vec3 pixel1 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n" + " vec3 pixel2 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n" + " vec3 pixel3 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n" + " vec3 pixel4 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n" + " vec3 pixel12 = pixel1 + pixel2;\n" + " vec3 pixel34 = pixel3 + pixel4;\n" + " gl_FragData[0] = vec4(dot(pixel1, kRGBtoY),\n" + " dot(pixel2, kRGBtoY),\n" + " dot(pixel3, kRGBtoY),\n" + " dot(pixel4, kRGBtoY)) + kYBias;\n" + " gl_FragData[1] = vec4(dot(pixel12, kRGBtoU),\n" + " dot(pixel34, kRGBtoU),\n" + " dot(pixel12, kRGBtoV),\n" + " dot(pixel34, kRGBtoV)) + kUVBias;\n"); + break; + + case SHADER_YUV_MRT_PASS2: + // We're just sampling two pixels and unswizzling them. There's + // no need to do vertical scaling with math, since bilinear + // interpolation in the sampler takes care of that. + shared_variables.append("varying vec4 v_texcoords;\n"); + vertex_header.append( + "uniform vec2 scaling_vector;\n" + "uniform vec2 dst_pixelsize;\n"); + vertex_program.append( + " vec2 step = scaling_vector * src_subrect.zw / dst_pixelsize;\n" + " step /= 2.0;\n" + " v_texcoords.xy = texcoord - step * 0.5;\n" + " v_texcoords.zw = texcoord + step * 0.5;\n"); + fragment_directives.append("#extension GL_EXT_draw_buffers : enable\n"); + fragment_program.append( + " vec4 lo_uuvv = texture2D(s_texture, v_texcoords.xy);\n" + " vec4 hi_uuvv = texture2D(s_texture, v_texcoords.zw);\n" + " gl_FragData[0] = vec4(lo_uuvv.rg, hi_uuvv.rg);\n" + " gl_FragData[1] = vec4(lo_uuvv.ba, hi_uuvv.ba);\n"); + break; + } + if (swizzle) { + switch (type) { + case SHADER_YUV_MRT_PASS1: + fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); + break; + case SHADER_YUV_MRT_PASS2: + fragment_program.append(" gl_FragData[0] = gl_FragData[0].bgra;\n"); + fragment_program.append(" gl_FragData[1] = gl_FragData[1].bgra;\n"); + break; + default: + fragment_program.append(" gl_FragColor = gl_FragColor.bgra;\n"); + break; + } + } + + vertex_program = vertex_header + shared_variables + "void main() {\n" + + vertex_program + "}\n"; + + fragment_program = fragment_directives + fragment_header + + shared_variables + "void main() {\n" + fragment_program + + "}\n"; + + cache_entry->Setup(vertex_program.c_str(), fragment_program.c_str()); + } + return cache_entry; +} + +void ShaderProgram::Setup(const GLchar* vertex_shader_text, + const GLchar* fragment_shader_text) { + // Shaders to map the source texture to |dst_texture_|. + GLuint vertex_shader = + helper_->CompileShaderFromSource(vertex_shader_text, GL_VERTEX_SHADER); + if (vertex_shader == 0) + return; + + gl_->AttachShader(program_, vertex_shader); + gl_->DeleteShader(vertex_shader); + + GLuint fragment_shader = helper_->CompileShaderFromSource( + fragment_shader_text, GL_FRAGMENT_SHADER); + if (fragment_shader == 0) + return; + gl_->AttachShader(program_, fragment_shader); + gl_->DeleteShader(fragment_shader); + + gl_->LinkProgram(program_); + + GLint link_status = 0; + gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status); + if (!link_status) + return; + + position_location_ = gl_->GetAttribLocation(program_, "a_position"); + texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord"); + texture_location_ = gl_->GetUniformLocation(program_, "s_texture"); + src_subrect_location_ = gl_->GetUniformLocation(program_, "src_subrect"); + src_pixelsize_location_ = gl_->GetUniformLocation(program_, "src_pixelsize"); + dst_pixelsize_location_ = gl_->GetUniformLocation(program_, "dst_pixelsize"); + scaling_vector_location_ = + gl_->GetUniformLocation(program_, "scaling_vector"); + color_weights_location_ = gl_->GetUniformLocation(program_, "color_weights"); + // The only reason fetching these attribute locations should fail is + // if the context was spontaneously lost (i.e., because the GPU + // process crashed, perhaps deliberately for testing). + DCHECK(Initialized() || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR); +} + +void ShaderProgram::UseProgram(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool scale_x, + bool flip_y, + GLfloat color_weights[4]) { + gl_->UseProgram(program_); + + // OpenGL defines the last parameter to VertexAttribPointer as type + // "const GLvoid*" even though it is actually an offset into the buffer + // object's data store and not a pointer to the client's address space. + const void* offsets[2] = {0, + reinterpret_cast(2 * sizeof(GLfloat))}; + + gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE, + 4 * sizeof(GLfloat), offsets[0]); + gl_->EnableVertexAttribArray(position_location_); + + gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE, + 4 * sizeof(GLfloat), offsets[1]); + gl_->EnableVertexAttribArray(texcoord_location_); + + gl_->Uniform1i(texture_location_, 0); + + // Convert |src_subrect| to texture coordinates. + GLfloat src_subrect_texcoord[] = { + static_cast(src_subrect.x()) / src_size.width(), + static_cast(src_subrect.y()) / src_size.height(), + static_cast(src_subrect.width()) / src_size.width(), + static_cast(src_subrect.height()) / src_size.height(), + }; + if (flip_y) { + src_subrect_texcoord[1] += src_subrect_texcoord[3]; + src_subrect_texcoord[3] *= -1.0; + } + gl_->Uniform4fv(src_subrect_location_, 1, src_subrect_texcoord); + + gl_->Uniform2f(src_pixelsize_location_, src_size.width(), src_size.height()); + gl_->Uniform2f(dst_pixelsize_location_, static_cast(dst_size.width()), + static_cast(dst_size.height())); + + gl_->Uniform2f(scaling_vector_location_, scale_x ? 1.0 : 0.0, + scale_x ? 0.0 : 1.0); + gl_->Uniform4fv(color_weights_location_, 1, color_weights); +} + +} // namespace content diff --git a/chromium/content/browser/compositor/gl_helper_scaling.h b/chromium/content/browser/compositor/gl_helper_scaling.h new file mode 100644 index 00000000000..6fbc03342ad --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_scaling.h @@ -0,0 +1,206 @@ +// 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 CONTENT_BROWSER_COMPOSITOR_GL_HELPER_SCALING_H_ +#define CONTENT_BROWSER_COMPOSITOR_GL_HELPER_SCALING_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "content/browser/compositor/gl_helper.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace content { + +class ShaderProgram; +class ScalerImpl; +class GLHelperTest; + +// Implements GPU texture scaling methods. +// Note that you should probably not use this class directly. +// See gl_helper.cc::CreateScaler instead. +class CONTENT_EXPORT GLHelperScaling { + public: + enum ShaderType { + SHADER_BILINEAR, + SHADER_BILINEAR2, + SHADER_BILINEAR3, + SHADER_BILINEAR4, + SHADER_BILINEAR2X2, + SHADER_BICUBIC_UPSCALE, + SHADER_BICUBIC_HALF_1D, + SHADER_PLANAR, + SHADER_YUV_MRT_PASS1, + SHADER_YUV_MRT_PASS2, + }; + + // Similar to ScalerInterface, but can generate multiple outputs. + // Used for YUV conversion in gl_helper.c + class CONTENT_EXPORT ShaderInterface { + public: + ShaderInterface() {} + virtual ~ShaderInterface() {} + // Note that the src_texture will have the min/mag filter set to GL_LINEAR + // and wrap_s/t set to CLAMP_TO_EDGE in this call. + virtual void Execute(GLuint source_texture, + const std::vector& dest_textures) = 0; + }; + + typedef std::pair ShaderProgramKeyType; + + GLHelperScaling(gpu::gles2::GLES2Interface* gl, GLHelper* helper); + ~GLHelperScaling(); + void InitBuffer(); + + GLHelper::ScalerInterface* CreateScaler(GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle); + + GLHelper::ScalerInterface* CreatePlanarScaler(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + const float color_weights[4]); + + ShaderInterface* CreateYuvMrtShader(const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + ShaderType shader); + + private: + // A ScaleOp represents a pass in a scaler pipeline, in one dimension. + // Note that when quality is GOOD, multiple scaler passes will be + // combined into one operation for increased performance. + // Exposed in the header file for testing purposes. + struct ScaleOp { + ScaleOp(int factor, bool x, int size) + : scale_factor(factor), scale_x(x), scale_size(size) {} + + // Calculate a set of ScaleOp needed to convert an image of size + // |src| into an image of size |dst|. If |scale_x| is true, then + // the calculations are for the X axis of the image, otherwise Y. + // If |allow3| is true, we can use a SHADER_BILINEAR3 to replace + // a scale up and scale down with a 3-tap bilinear scale. + // The calculated ScaleOps are added to |ops|. + static void AddOps(int src, + int dst, + bool scale_x, + bool allow3, + std::deque* ops) { + int num_downscales = 0; + if (allow3 && dst * 3 >= src && dst * 2 < src) { + // Technically, this should be a scale up and then a + // scale down, but it makes the optimization code more + // complicated. + ops->push_back(ScaleOp(3, scale_x, dst)); + return; + } + while ((dst << num_downscales) < src) { + num_downscales++; + } + if ((dst << num_downscales) != src) { + ops->push_back(ScaleOp(0, scale_x, dst << num_downscales)); + } + while (num_downscales) { + num_downscales--; + ops->push_back(ScaleOp(2, scale_x, dst << num_downscales)); + } + } + + // Update |size| to its new size. Before calling this function + // |size| should be the size of the input image. After calling it, + // |size| will be the size of the image after this particular + // scaling operation. + void UpdateSize(gfx::Size* subrect) { + if (scale_x) { + subrect->set_width(scale_size); + } else { + subrect->set_height(scale_size); + } + } + + // A scale factor of 0 means upscale + // 2 means 50% scale + // 3 means 33% scale, etc. + int scale_factor; + bool scale_x; // Otherwise y + int scale_size; // Size to scale to. + }; + + // Full specification for a single scaling stage. + struct ScalerStage { + ScalerStage(ShaderType shader_, + gfx::Size src_size_, + gfx::Rect src_subrect_, + gfx::Size dst_size_, + bool scale_x_, + bool vertically_flip_texture_, + bool swizzle_); + ScalerStage(const ScalerStage& other); + ShaderType shader; + gfx::Size src_size; + gfx::Rect src_subrect; + gfx::Size dst_size; + bool scale_x; + bool vertically_flip_texture; + bool swizzle; + }; + + // Compute a vector of scaler stages for a particular + // set of input/output parameters. + void ComputeScalerStages(GLHelper::ScalerQuality quality, + const gfx::Size& src_size, + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::vector* scaler_stages); + + // Take two queues of ScaleOp structs and generate a + // vector of scaler stages. This is the second half of + // ComputeScalerStages. + void ConvertScalerOpsToScalerStages( + GLHelper::ScalerQuality quality, + gfx::Size src_size, + gfx::Rect src_subrect, + const gfx::Size& dst_size, + bool vertically_flip_texture, + bool swizzle, + std::deque* x_ops, + std::deque* y_ops, + std::vector* scaler_stages); + + scoped_refptr GetShaderProgram(ShaderType type, bool swizzle); + + // Interleaved array of 2-dimentional vertex positions (x, y) and + // 2-dimentional texture coordinates (s, t). + static const GLfloat kVertexAttributes[]; + + gpu::gles2::GLES2Interface* gl_; + GLHelper* helper_; + + // The buffer that holds the vertices and the texture coordinates data for + // drawing a quad. + ScopedBuffer vertex_attributes_buffer_; + + std::map> shader_programs_; + + friend class ShaderProgram; + friend class ScalerImpl; + friend class GLHelperTest; + DISALLOW_COPY_AND_ASSIGN(GLHelperScaling); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COMPOSITOR_GL_HELPER_SCALING_H_ diff --git a/chromium/content/browser/compositor/gl_helper_unittest.cc b/chromium/content/browser/compositor/gl_helper_unittest.cc new file mode 100644 index 00000000000..697c68ba47a --- /dev/null +++ b/chromium/content/browser/compositor/gl_helper_unittest.cc @@ -0,0 +1,1828 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/waitable_event.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "content/browser/compositor/gl_helper.h" +#include "content/browser/compositor/gl_helper_readback_support.h" +#include "content/browser/compositor/gl_helper_scaling.h" +#include "gpu/command_buffer/client/gl_in_process_context.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "media/base/video_frame.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkTypes.h" +#include "ui/gl/gl_implementation.h" + +namespace content { + +content::GLHelper::ScalerQuality kQualities[] = { + content::GLHelper::SCALER_QUALITY_BEST, + content::GLHelper::SCALER_QUALITY_GOOD, + content::GLHelper::SCALER_QUALITY_FAST, +}; + +const char* kQualityNames[] = { + "best", "good", "fast", +}; + +class GLHelperTest : public testing::Test { + protected: + void SetUp() override { + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = 8; + attributes.depth_size = 24; + attributes.red_size = 8; + attributes.green_size = 8; + attributes.blue_size = 8; + attributes.stencil_size = 8; + attributes.samples = 4; + attributes.sample_buffers = 1; + attributes.bind_generates_resource = false; + + context_.reset(gpu::GLInProcessContext::Create( + nullptr, /* service */ + nullptr, /* surface */ + true, /* offscreen */ + gfx::kNullAcceleratedWidget, /* window */ + gfx::Size(1, 1), /* size */ + nullptr, /* share_context */ + attributes, gfx::PreferDiscreteGpu, + ::gpu::GLInProcessContextSharedMemoryLimits(), + nullptr, /* gpu_memory_buffer_manager */ + nullptr /* image_factory */)); + gl_ = context_->GetImplementation(); + gpu::ContextSupport* support = context_->GetImplementation(); + + helper_.reset(new content::GLHelper(gl_, support)); + helper_scaling_.reset(new content::GLHelperScaling(gl_, helper_.get())); + } + + void TearDown() override { + helper_scaling_.reset(NULL); + helper_.reset(NULL); + context_.reset(NULL); + } + + void StartTracing(const std::string& filter) { + base::trace_event::TraceLog::GetInstance()->SetEnabled( + base::trace_event::TraceConfig(filter, + base::trace_event::RECORD_UNTIL_FULL), + base::trace_event::TraceLog::RECORDING_MODE); + } + + static void TraceDataCB( + const base::Callback& callback, + std::string* output, + const scoped_refptr& json_events_str, + bool has_more_events) { + if (output->size() > 1 && !json_events_str->data().empty()) { + output->append(","); + } + output->append(json_events_str->data()); + if (!has_more_events) { + callback.Run(); + } + } + + // End tracing, return tracing data in a simple map + // of event name->counts. + void EndTracing(std::map* event_counts) { + std::string json_data = "["; + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + base::RunLoop run_loop; + base::trace_event::TraceLog::GetInstance()->Flush( + base::Bind(&GLHelperTest::TraceDataCB, run_loop.QuitClosure(), + base::Unretained(&json_data))); + run_loop.Run(); + json_data.append("]"); + + std::string error_msg; + scoped_ptr trace_data = + base::JSONReader::ReadAndReturnError(json_data, 0, NULL, &error_msg); + CHECK(trace_data) << "JSON parsing failed (" << error_msg + << ") JSON data:" << std::endl + << json_data; + + base::ListValue* list; + CHECK(trace_data->GetAsList(&list)); + for (size_t i = 0; i < list->GetSize(); i++) { + base::Value* item = NULL; + if (list->Get(i, &item)) { + base::DictionaryValue* dict; + CHECK(item->GetAsDictionary(&dict)); + std::string name; + CHECK(dict->GetString("name", &name)); + std::string trace_type; + CHECK(dict->GetString("ph", &trace_type)); + // Count all except END traces, as they come in BEGIN/END pairs. + if (trace_type != "E" && trace_type != "e") + (*event_counts)[name]++; + VLOG(1) << "trace name: " << name; + } + } + } + + // Bicubic filter kernel function. + static float Bicubic(float x) { + const float a = -0.5; + x = std::abs(x); + float x2 = x * x; + float x3 = x2 * x; + if (x <= 1) { + return (a + 2) * x3 - (a + 3) * x2 + 1; + } else if (x < 2) { + return a * x3 - 5 * a * x2 + 8 * a * x - 4 * a; + } else { + return 0.0f; + } + } + + // Look up a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + int Channel(SkBitmap* pixels, int x, int y, int c) { + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = + pixels->getAddr32(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + return (*data) >> (c * 8) & 0xff; + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + return *pixels->getAddr8(std::max(0, std::min(x, pixels->width() - 1)), + std::max(0, std::min(y, pixels->height() - 1))); + } + } + + // Set a single channel value. Works for 4-channel and single channel + // bitmaps. Clamp x/y. + void SetChannel(SkBitmap* pixels, int x, int y, int c, int v) { + DCHECK_GE(x, 0); + DCHECK_GE(y, 0); + DCHECK_LT(x, pixels->width()); + DCHECK_LT(y, pixels->height()); + if (pixels->bytesPerPixel() == 4) { + uint32_t* data = pixels->getAddr32(x, y); + v = std::max(0, std::min(v, 255)); + *data = (*data & ~(0xffu << (c * 8))) | (v << (c * 8)); + } else { + DCHECK_EQ(pixels->bytesPerPixel(), 1); + DCHECK_EQ(c, 0); + uint8_t* data = pixels->getAddr8(x, y); + v = std::max(0, std::min(v, 255)); + *data = v; + } + } + + // Print all the R, G, B or A values from an SkBitmap in a + // human-readable format. + void PrintChannel(SkBitmap* pixels, int c) { + for (int y = 0; y < pixels->height(); y++) { + std::string formatted; + for (int x = 0; x < pixels->width(); x++) { + formatted.append(base::StringPrintf("%3d, ", Channel(pixels, x, y, c))); + } + LOG(ERROR) << formatted; + } + } + + // Print out the individual steps of a scaler pipeline. + std::string PrintStages( + const std::vector& scaler_stages) { + std::string ret; + for (size_t i = 0; i < scaler_stages.size(); i++) { + ret.append(base::StringPrintf( + "%dx%d -> %dx%d ", scaler_stages[i].src_size.width(), + scaler_stages[i].src_size.height(), scaler_stages[i].dst_size.width(), + scaler_stages[i].dst_size.height())); + bool xy_matters = false; + switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_BILINEAR: + ret.append("bilinear"); + break; + case GLHelperScaling::SHADER_BILINEAR2: + ret.append("bilinear2"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR3: + ret.append("bilinear3"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR4: + ret.append("bilinear4"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BILINEAR2X2: + ret.append("bilinear2x2"); + break; + case GLHelperScaling::SHADER_BICUBIC_UPSCALE: + ret.append("bicubic upscale"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_BICUBIC_HALF_1D: + ret.append("bicubic 1/2"); + xy_matters = true; + break; + case GLHelperScaling::SHADER_PLANAR: + ret.append("planar"); + break; + case GLHelperScaling::SHADER_YUV_MRT_PASS1: + ret.append("rgb2yuv pass 1"); + break; + case GLHelperScaling::SHADER_YUV_MRT_PASS2: + ret.append("rgb2yuv pass 2"); + break; + } + + if (xy_matters) { + if (scaler_stages[i].scale_x) { + ret.append(" X"); + } else { + ret.append(" Y"); + } + } + ret.append("\n"); + } + return ret; + } + + bool CheckScale(double scale, int samples, bool already_scaled) { + // 1:1 is valid if there is one sample. + if (samples == 1 && scale == 1.0) { + return true; + } + // Is it an exact down-scale (50%, 25%, etc.?) + if (scale == 2.0 * samples) { + return true; + } + // Upscales, only valid if we haven't already scaled in this dimension. + if (!already_scaled) { + // Is it a valid bilinear upscale? + if (samples == 1 && scale <= 1.0) { + return true; + } + // Multi-sample upscale-downscale combination? + if (scale > samples / 2.0 && scale < samples) { + return true; + } + } + return false; + } + + // Make sure that the stages of the scaler pipeline are sane. + void ValidateScalerStages( + content::GLHelper::ScalerQuality quality, + const std::vector& scaler_stages, + const gfx::Size& dst_size, + const std::string& message) { + bool previous_error = HasFailure(); + // First, check that the input size for each stage is equal to + // the output size of the previous stage. + for (size_t i = 1; i < scaler_stages.size(); i++) { + EXPECT_EQ(scaler_stages[i - 1].dst_size.width(), + scaler_stages[i].src_size.width()); + EXPECT_EQ(scaler_stages[i - 1].dst_size.height(), + scaler_stages[i].src_size.height()); + EXPECT_EQ(scaler_stages[i].src_subrect.x(), 0); + EXPECT_EQ(scaler_stages[i].src_subrect.y(), 0); + EXPECT_EQ(scaler_stages[i].src_subrect.width(), + scaler_stages[i].src_size.width()); + EXPECT_EQ(scaler_stages[i].src_subrect.height(), + scaler_stages[i].src_size.height()); + } + + // Check the output size matches the destination of the last stage + EXPECT_EQ(scaler_stages[scaler_stages.size() - 1].dst_size.width(), + dst_size.width()); + EXPECT_EQ(scaler_stages[scaler_stages.size() - 1].dst_size.height(), + dst_size.height()); + + // Used to verify that up-scales are not attempted after some + // other scale. + bool scaled_x = false; + bool scaled_y = false; + + for (size_t i = 0; i < scaler_stages.size(); i++) { + // Note: 2.0 means scaling down by 50% + double x_scale = + static_cast(scaler_stages[i].src_subrect.width()) / + static_cast(scaler_stages[i].dst_size.width()); + double y_scale = + static_cast(scaler_stages[i].src_subrect.height()) / + static_cast(scaler_stages[i].dst_size.height()); + + int x_samples = 0; + int y_samples = 0; + + // Codify valid scale operations. + switch (scaler_stages[i].shader) { + case GLHelperScaling::SHADER_PLANAR: + case GLHelperScaling::SHADER_YUV_MRT_PASS1: + case GLHelperScaling::SHADER_YUV_MRT_PASS2: + EXPECT_TRUE(false) << "Invalid shader."; + break; + + case GLHelperScaling::SHADER_BILINEAR: + if (quality != content::GLHelper::SCALER_QUALITY_FAST) { + x_samples = 1; + y_samples = 1; + } + break; + case GLHelperScaling::SHADER_BILINEAR2: + x_samples = 2; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR3: + x_samples = 3; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR4: + x_samples = 4; + y_samples = 1; + break; + case GLHelperScaling::SHADER_BILINEAR2X2: + x_samples = 2; + y_samples = 2; + break; + case GLHelperScaling::SHADER_BICUBIC_UPSCALE: + if (scaler_stages[i].scale_x) { + EXPECT_LT(x_scale, 1.0); + EXPECT_EQ(y_scale, 1.0); + } else { + EXPECT_EQ(x_scale, 1.0); + EXPECT_LT(y_scale, 1.0); + } + break; + case GLHelperScaling::SHADER_BICUBIC_HALF_1D: + if (scaler_stages[i].scale_x) { + EXPECT_EQ(x_scale, 2.0); + EXPECT_EQ(y_scale, 1.0); + } else { + EXPECT_EQ(x_scale, 1.0); + EXPECT_EQ(y_scale, 2.0); + } + break; + } + + if (!scaler_stages[i].scale_x) { + std::swap(x_samples, y_samples); + } + + if (x_samples) { + EXPECT_TRUE(CheckScale(x_scale, x_samples, scaled_x)) << "x_scale = " + << x_scale; + } + if (y_samples) { + EXPECT_TRUE(CheckScale(y_scale, y_samples, scaled_y)) << "y_scale = " + << y_scale; + } + + if (x_scale != 1.0) { + scaled_x = true; + } + if (y_scale != 1.0) { + scaled_y = true; + } + } + + if (HasFailure() && !previous_error) { + LOG(ERROR) << "Invalid scaler stages: " << message; + LOG(ERROR) << "Scaler stages:"; + LOG(ERROR) << PrintStages(scaler_stages); + } + } + + // Compares two bitmaps taking color types into account. Checks whether each + // component of each pixel is no more than |maxdiff| apart. If bitmaps are not + // similar enough, prints out |truth|, |other|, |source|, |scaler_stages| + // and |message|. + void Compare(SkBitmap* truth, + SkBitmap* other, + int maxdiff, + SkBitmap* source, + const std::vector& scaler_stages, + std::string message) { + EXPECT_EQ(truth->width(), other->width()); + EXPECT_EQ(truth->height(), other->height()); + bool swizzle = (truth->colorType() == kRGBA_8888_SkColorType && + other->colorType() == kBGRA_8888_SkColorType) || + (truth->colorType() == kBGRA_8888_SkColorType && + other->colorType() == kRGBA_8888_SkColorType); + EXPECT_TRUE(swizzle || truth->colorType() == other->colorType()); + int bpp = truth->bytesPerPixel(); + for (int x = 0; x < truth->width(); x++) { + for (int y = 0; y < truth->height(); y++) { + for (int c = 0; c < bpp; c++) { + int a = Channel(truth, x, y, c); + // swizzle when comparing if needed + int b = swizzle && (c == 0 || c == 2) + ? Channel(other, x, y, (c + 2) & 2) + : Channel(other, x, y, c); + EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " c=" << c + << " " << message; + if (std::abs(a - b) > maxdiff) { + LOG(ERROR) << "-------expected--------"; + for (int i = 0; i < bpp; i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(truth, i); + } + LOG(ERROR) << "-------actual--------"; + for (int i = 0; i < bpp; i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(other, i); + } + if (source) { + LOG(ERROR) << "-------original--------"; + for (int i = 0; i < source->bytesPerPixel(); i++) { + LOG(ERROR) << "Channel " << i << ":"; + PrintChannel(source, i); + } + } + LOG(ERROR) << "-----Scaler stages------"; + LOG(ERROR) << PrintStages(scaler_stages); + return; + } + } + } + } + } + + // Get a single R, G, B or A value as a float. + float ChannelAsFloat(SkBitmap* pixels, int x, int y, int c) { + return Channel(pixels, x, y, c) / 255.0; + } + + // Works like a GL_LINEAR lookup on an SkBitmap. + float Bilinear(SkBitmap* pixels, float x, float y, int c) { + x -= 0.5; + y -= 0.5; + int base_x = static_cast(floorf(x)); + int base_y = static_cast(floorf(y)); + x -= base_x; + y -= base_y; + return (ChannelAsFloat(pixels, base_x, base_y, c) * (1 - x) * (1 - y) + + ChannelAsFloat(pixels, base_x + 1, base_y, c) * x * (1 - y) + + ChannelAsFloat(pixels, base_x, base_y + 1, c) * (1 - x) * y + + ChannelAsFloat(pixels, base_x + 1, base_y + 1, c) * x * y); + } + + // Encodes an RGBA bitmap to grayscale. + // Reference implementation for + // GLHelper::CopyToTextureImpl::EncodeTextureAsGrayscale. + void EncodeToGrayscaleSlow(SkBitmap* input, SkBitmap* output) { + const float kRGBtoGrayscaleColorWeights[3] = {0.213f, 0.715f, 0.072f}; + CHECK_EQ(kAlpha_8_SkColorType, output->colorType()); + CHECK_EQ(input->width(), output->width()); + CHECK_EQ(input->height(), output->height()); + CHECK_EQ(input->colorType(), kRGBA_8888_SkColorType); + + for (int dst_y = 0; dst_y < output->height(); dst_y++) { + for (int dst_x = 0; dst_x < output->width(); dst_x++) { + float c0 = ChannelAsFloat(input, dst_x, dst_y, 0); + float c1 = ChannelAsFloat(input, dst_x, dst_y, 1); + float c2 = ChannelAsFloat(input, dst_x, dst_y, 2); + float value = c0 * kRGBtoGrayscaleColorWeights[0] + + c1 * kRGBtoGrayscaleColorWeights[1] + + c2 * kRGBtoGrayscaleColorWeights[2]; + SetChannel(output, dst_x, dst_y, 0, + static_cast(value * 255.0f + 0.5f)); + } + } + } + + // Very slow bicubic / bilinear scaler for reference. + void ScaleSlow(SkBitmap* input, + SkBitmap* output, + content::GLHelper::ScalerQuality quality) { + float xscale = static_cast(input->width()) / output->width(); + float yscale = static_cast(input->height()) / output->height(); + float clamped_xscale = xscale < 1.0 ? 1.0 : 1.0 / xscale; + float clamped_yscale = yscale < 1.0 ? 1.0 : 1.0 / yscale; + for (int dst_y = 0; dst_y < output->height(); dst_y++) { + for (int dst_x = 0; dst_x < output->width(); dst_x++) { + for (int channel = 0; channel < 4; channel++) { + float dst_x_in_src = (dst_x + 0.5f) * xscale; + float dst_y_in_src = (dst_y + 0.5f) * yscale; + + float value = 0.0f; + float sum = 0.0f; + switch (quality) { + case content::GLHelper::SCALER_QUALITY_BEST: + for (int src_y = -10; src_y < input->height() + 10; ++src_y) { + float coeff_y = + Bicubic((src_y + 0.5f - dst_y_in_src) * clamped_yscale); + if (coeff_y == 0.0f) { + continue; + } + for (int src_x = -10; src_x < input->width() + 10; ++src_x) { + float coeff = + coeff_y * + Bicubic((src_x + 0.5f - dst_x_in_src) * clamped_xscale); + if (coeff == 0.0f) { + continue; + } + sum += coeff; + float c = ChannelAsFloat(input, src_x, src_y, channel); + value += c * coeff; + } + } + break; + + case content::GLHelper::SCALER_QUALITY_GOOD: { + int xshift = 0, yshift = 0; + while ((output->width() << xshift) < input->width()) { + xshift++; + } + while ((output->height() << yshift) < input->height()) { + yshift++; + } + int xmag = 1 << xshift; + int ymag = 1 << yshift; + if (xmag == 4 && output->width() * 3 >= input->width()) { + xmag = 3; + } + if (ymag == 4 && output->height() * 3 >= input->height()) { + ymag = 3; + } + for (int x = 0; x < xmag; x++) { + for (int y = 0; y < ymag; y++) { + value += Bilinear( + input, (dst_x * xmag + x + 0.5) * xscale / xmag, + (dst_y * ymag + y + 0.5) * yscale / ymag, channel); + sum += 1.0; + } + } + break; + } + + case content::GLHelper::SCALER_QUALITY_FAST: + value = Bilinear(input, dst_x_in_src, dst_y_in_src, channel); + sum = 1.0; + } + value /= sum; + SetChannel(output, dst_x, dst_y, channel, + static_cast(value * 255.0f + 0.5f)); + } + } + } + } + + void FlipSKBitmap(SkBitmap* bitmap) { + int bpp = bitmap->bytesPerPixel(); + DCHECK(bpp == 4 || bpp == 1); + int top_line = 0; + int bottom_line = bitmap->height() - 1; + while (top_line < bottom_line) { + for (int x = 0; x < bitmap->width(); x++) { + bpp == 4 ? std::swap(*bitmap->getAddr32(x, top_line), + *bitmap->getAddr32(x, bottom_line)) + : std::swap(*bitmap->getAddr8(x, top_line), + *bitmap->getAddr8(x, bottom_line)); + } + top_line++; + bottom_line--; + } + } + + // Swaps red and blue channels in each pixel in a 32-bit bitmap. + void SwizzleSKBitmap(SkBitmap* bitmap) { + int bpp = bitmap->bytesPerPixel(); + DCHECK(bpp == 4); + for (int y = 0; y < bitmap->height(); y++) { + for (int x = 0; x < bitmap->width(); x++) { + // Swap channels 0 and 2 (red and blue) + int c0 = Channel(bitmap, x, y, 0); + int c2 = Channel(bitmap, x, y, 2); + SetChannel(bitmap, x, y, 2, c0); + SetChannel(bitmap, x, y, 0, c2); + } + } + } + + // gl_helper scales recursively, so we'll need to do that + // in the reference implementation too. + void ScaleSlowRecursive(SkBitmap* input, + SkBitmap* output, + content::GLHelper::ScalerQuality quality) { + if (quality == content::GLHelper::SCALER_QUALITY_FAST || + quality == content::GLHelper::SCALER_QUALITY_GOOD) { + ScaleSlow(input, output, quality); + return; + } + + float xscale = static_cast(output->width()) / input->width(); + + // This corresponds to all the operations we can do directly. + float yscale = static_cast(output->height()) / input->height(); + if ((xscale == 1.0f && yscale == 1.0f) || + (xscale == 0.5f && yscale == 1.0f) || + (xscale == 1.0f && yscale == 0.5f) || + (xscale >= 1.0f && yscale == 1.0f) || + (xscale == 1.0f && yscale >= 1.0f)) { + ScaleSlow(input, output, quality); + return; + } + + // Now we break the problem down into smaller pieces, using the + // operations available. + int xtmp = input->width(); + int ytmp = input->height(); + + if (output->height() != input->height()) { + ytmp = output->height(); + while (ytmp < input->height() && ytmp * 2 != input->height()) { + ytmp += ytmp; + } + } else { + xtmp = output->width(); + while (xtmp < input->width() && xtmp * 2 != input->width()) { + xtmp += xtmp; + } + } + + SkBitmap tmp; + tmp.allocN32Pixels(xtmp, ytmp); + + ScaleSlowRecursive(input, &tmp, quality); + ScaleSlowRecursive(&tmp, output, quality); + } + + // Creates an RGBA SkBitmap + scoped_ptr CreateTestBitmap(int width, + int height, + int test_pattern) { + scoped_ptr bitmap(new SkBitmap); + bitmap->allocPixels(SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(bitmap.get(), x, y, 0, x * 10); + SetChannel(bitmap.get(), x, y, 0, y == 0 ? x * 50 : x * 10); + SetChannel(bitmap.get(), x, y, 1, y * 10); + SetChannel(bitmap.get(), x, y, 2, (x + y) * 10); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(bitmap.get(), x, y, 0, x & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 1, y & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(bitmap.get(), x, y, 0, 10 + x / 2 * 50); + SetChannel(bitmap.get(), x, y, 1, 10 + y / 3 * 50); + SetChannel(bitmap.get(), x, y, 2, (x + y) / 5 * 50 + 5); + SetChannel(bitmap.get(), x, y, 3, 255); + break; + } + } + } + return bitmap; + } + + // Binds texture and framebuffer and loads the bitmap pixels into the texture. + void BindTextureAndFrameBuffer(GLuint texture, + GLuint framebuffer, + SkBitmap* bitmap, + int width, + int height) { + gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl_->BindTexture(GL_TEXTURE_2D, texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + } + + // Create a test image, transform it using + // GLHelper::CropScaleReadbackAndCleanTexture and a reference implementation + // and compare the results. + void TestCropScaleReadbackAndCleanTexture(int xsize, + int ysize, + int scaled_xsize, + int scaled_ysize, + int test_pattern, + SkColorType out_color_type, + bool swizzle, + size_t quality_index) { + DCHECK(out_color_type == kAlpha_8_SkColorType || + out_color_type == kRGBA_8888_SkColorType || + out_color_type == kBGRA_8888_SkColorType); + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + GLuint framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + scoped_ptr input_pixels = + CreateTestBitmap(xsize, ysize, test_pattern); + BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(), + xsize, ysize); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "pattern: %d , quality: %s, " + "out_color_type: %d", + xsize, ysize, scaled_xsize, scaled_ysize, test_pattern, + kQualityNames[quality_index], out_color_type); + + // Transform the bitmap using GLHelper::CropScaleReadbackAndCleanTexture. + SkBitmap output_pixels; + output_pixels.allocPixels(SkImageInfo::Make( + scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType)); + base::RunLoop run_loop; + gfx::Size encoded_texture_size; + helper_->CropScaleReadbackAndCleanTexture( + src_texture, gfx::Size(xsize, ysize), gfx::Rect(xsize, ysize), + gfx::Size(scaled_xsize, scaled_ysize), + static_cast(output_pixels.getPixels()), out_color_type, + base::Bind(&callcallback, run_loop.QuitClosure()), + kQualities[quality_index]); + run_loop.Run(); + // CropScaleReadbackAndCleanTexture flips the pixels. Flip them back. + FlipSKBitmap(&output_pixels); + + // If the bitmap shouldn't have changed - compare against input. + if (xsize == scaled_xsize && ysize == scaled_ysize && + out_color_type != kAlpha_8_SkColorType) { + const std::vector dummy_stages; + Compare(input_pixels.get(), &output_pixels, 0, NULL, dummy_stages, + message + " comparing against input"); + return; + } + + // Now transform the bitmap using the reference implementation. + SkBitmap scaled_pixels; + scaled_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + SkBitmap truth_pixels; + // Step 1: Scale + ScaleSlowRecursive(input_pixels.get(), &scaled_pixels, + kQualities[quality_index]); + // Step 2: Encode to grayscale if needed. + if (out_color_type == kAlpha_8_SkColorType) { + truth_pixels.allocPixels(SkImageInfo::Make( + scaled_xsize, scaled_ysize, out_color_type, kPremul_SkAlphaType)); + EncodeToGrayscaleSlow(&scaled_pixels, &truth_pixels); + } else { + truth_pixels = scaled_pixels; + } + + // Now compare the results. + SkAutoLockPixels lock_input(truth_pixels); + const std::vector dummy_stages; + Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), dummy_stages, + message + " comparing against transformed/scaled"); + + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + } + + // Scaling test: Create a test image, scale it using GLHelperScaling + // and a reference implementation and compare the results. + void TestScale(int xsize, + int ysize, + int scaled_xsize, + int scaled_ysize, + int test_pattern, + size_t quality_index, + bool flip) { + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + GLuint framebuffer; + gl_->GenFramebuffers(1, &framebuffer); + scoped_ptr input_pixels = + CreateTestBitmap(xsize, ysize, test_pattern); + BindTextureAndFrameBuffer(src_texture, framebuffer, input_pixels.get(), + xsize, ysize); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "pattern: %d quality: %s", + xsize, ysize, scaled_xsize, scaled_ysize, test_pattern, + kQualityNames[quality_index]); + + std::vector stages; + helper_scaling_->ComputeScalerStages(kQualities[quality_index], + gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), + gfx::Size(scaled_xsize, scaled_ysize), + flip, + false, + &stages); + ValidateScalerStages(kQualities[quality_index], + stages, + gfx::Size(scaled_xsize, scaled_ysize), + message); + + GLuint dst_texture = helper_->CopyAndScaleTexture( + src_texture, gfx::Size(xsize, ysize), + gfx::Size(scaled_xsize, scaled_ysize), flip, kQualities[quality_index]); + + SkBitmap output_pixels; + output_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + + helper_->ReadbackTextureSync( + dst_texture, gfx::Rect(0, 0, scaled_xsize, scaled_ysize), + static_cast(output_pixels.getPixels()), + kRGBA_8888_SkColorType); + if (flip) { + // Flip the pixels back. + FlipSKBitmap(&output_pixels); + } + + // If the bitmap shouldn't have changed - compare against input. + if (xsize == scaled_xsize && ysize == scaled_ysize) { + Compare(input_pixels.get(), &output_pixels, 0, NULL, stages, + message + " comparing against input"); + return; + } + + // Now scale the bitmap using the reference implementation. + SkBitmap truth_pixels; + truth_pixels.allocPixels(SkImageInfo::Make(scaled_xsize, scaled_ysize, + kRGBA_8888_SkColorType, + kPremul_SkAlphaType)); + ScaleSlowRecursive(input_pixels.get(), &truth_pixels, + kQualities[quality_index]); + Compare(&truth_pixels, &output_pixels, 2, input_pixels.get(), stages, + message + " comparing against scaled"); + + gl_->DeleteTextures(1, &src_texture); + gl_->DeleteTextures(1, &dst_texture); + gl_->DeleteFramebuffers(1, &framebuffer); + } + + // Create a scaling pipeline and check that it is made up of + // valid scaling operations. + void TestScalerPipeline(size_t quality, + int xsize, + int ysize, + int dst_xsize, + int dst_ysize) { + std::vector stages; + helper_scaling_->ComputeScalerStages( + kQualities[quality], gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false, + false, &stages); + ValidateScalerStages(kQualities[quality], stages, + gfx::Size(dst_xsize, dst_ysize), + base::StringPrintf("input size: %dx%d " + "output size: %dx%d " + "quality: %s", + xsize, ysize, dst_xsize, dst_ysize, + kQualityNames[quality])); + } + + // Create a scaling pipeline and make sure that the steps + // are exactly the steps we expect. + void CheckPipeline(content::GLHelper::ScalerQuality quality, + int xsize, + int ysize, + int dst_xsize, + int dst_ysize, + const std::string& description) { + std::vector stages; + helper_scaling_->ComputeScalerStages( + quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize), + gfx::Size(dst_xsize, dst_ysize), false, false, &stages); + ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD, stages, + gfx::Size(dst_xsize, dst_ysize), ""); + EXPECT_EQ(PrintStages(stages), description); + } + + // Note: Left/Right means Top/Bottom when used for Y dimension. + enum Margin { + MarginLeft, + MarginMiddle, + MarginRight, + MarginInvalid, + }; + + static Margin NextMargin(Margin m) { + switch (m) { + case MarginLeft: + return MarginMiddle; + case MarginMiddle: + return MarginRight; + case MarginRight: + return MarginInvalid; + default: + return MarginInvalid; + } + } + + int compute_margin(int insize, int outsize, Margin m) { + int available = outsize - insize; + switch (m) { + default: + EXPECT_TRUE(false) << "This should not happen."; + return 0; + case MarginLeft: + return 0; + case MarginMiddle: + return (available / 2) & ~1; + case MarginRight: + return available; + } + } + + // Convert 0.0 - 1.0 to 0 - 255 + int float_to_byte(float v) { + int ret = static_cast(floorf(v * 255.0f + 0.5f)); + if (ret < 0) { + return 0; + } + if (ret > 255) { + return 255; + } + return ret; + } + + static void callcallback(const base::Callback& callback, + bool result) { + callback.Run(); + } + + void PrintPlane(unsigned char* plane, int xsize, int stride, int ysize) { + for (int y = 0; y < ysize; y++) { + std::string formatted; + for (int x = 0; x < xsize; x++) { + formatted.append(base::StringPrintf("%3d, ", plane[y * stride + x])); + } + LOG(ERROR) << formatted << " (" << (plane + y * stride) << ")"; + } + } + + // Compare two planes make sure that each component of each pixel + // is no more than |maxdiff| apart. + void ComparePlane(unsigned char* truth, + int truth_stride, + unsigned char* other, + int other_stride, + int maxdiff, + int xsize, + int ysize, + SkBitmap* source, + std::string message) { + for (int x = 0; x < xsize; x++) { + for (int y = 0; y < ysize; y++) { + int a = other[y * other_stride + x]; + int b = truth[y * truth_stride + x]; + EXPECT_NEAR(a, b, maxdiff) << " x=" << x << " y=" << y << " " + << message; + if (std::abs(a - b) > maxdiff) { + LOG(ERROR) << "-------expected--------"; + PrintPlane(truth, xsize, truth_stride, ysize); + LOG(ERROR) << "-------actual--------"; + PrintPlane(other, xsize, other_stride, ysize); + if (source) { + LOG(ERROR) << "-------before yuv conversion: red--------"; + PrintChannel(source, 0); + LOG(ERROR) << "-------before yuv conversion: green------"; + PrintChannel(source, 1); + LOG(ERROR) << "-------before yuv conversion: blue-------"; + PrintChannel(source, 2); + } + return; + } + } + } + } + + void DrawGridToBitmap(int w, + int h, + SkColor background_color, + SkColor grid_color, + int grid_pitch, + int grid_width, + SkBitmap& bmp) { + ASSERT_GT(grid_pitch, 0); + ASSERT_GT(grid_width, 0); + ASSERT_NE(background_color, grid_color); + + for (int y = 0; y < h; ++y) { + bool y_on_grid = ((y % grid_pitch) < grid_width); + + for (int x = 0; x < w; ++x) { + bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width)); + + if (bmp.colorType() == kRGBA_8888_SkColorType || + bmp.colorType() == kBGRA_8888_SkColorType) { + *bmp.getAddr32(x, y) = (on_grid ? grid_color : background_color); + } else if (bmp.colorType() == kRGB_565_SkColorType) { + *bmp.getAddr16(x, y) = (on_grid ? grid_color : background_color); + } + } + } + } + + void DrawCheckerToBitmap(int w, + int h, + SkColor color1, + SkColor color2, + int rect_w, + int rect_h, + SkBitmap& bmp) { + ASSERT_GT(rect_w, 0); + ASSERT_GT(rect_h, 0); + ASSERT_NE(color1, color2); + + for (int y = 0; y < h; ++y) { + bool y_bit = (((y / rect_h) & 0x1) == 0); + + for (int x = 0; x < w; ++x) { + bool x_bit = (((x / rect_w) & 0x1) == 0); + + bool use_color2 = (x_bit != y_bit); // xor + if (bmp.colorType() == kRGBA_8888_SkColorType || + bmp.colorType() == kBGRA_8888_SkColorType) { + *bmp.getAddr32(x, y) = (use_color2 ? color2 : color1); + } else if (bmp.colorType() == kRGB_565_SkColorType) { + *bmp.getAddr16(x, y) = (use_color2 ? color2 : color1); + } + } + } + } + + bool ColorComponentsClose(SkColor component1, + SkColor component2, + SkColorType color_type) { + int c1 = static_cast(component1); + int c2 = static_cast(component2); + bool result = false; + switch (color_type) { + case kRGBA_8888_SkColorType: + case kBGRA_8888_SkColorType: + result = (std::abs(c1 - c2) == 0); + break; + case kRGB_565_SkColorType: + result = (std::abs(c1 - c2) <= 7); + break; + default: + break; + } + return result; + } + + bool ColorsClose(SkColor color1, SkColor color2, SkColorType color_type) { + bool red = ColorComponentsClose(SkColorGetR(color1), SkColorGetR(color2), + color_type); + bool green = ColorComponentsClose(SkColorGetG(color1), SkColorGetG(color2), + color_type); + bool blue = ColorComponentsClose(SkColorGetB(color1), SkColorGetB(color2), + color_type); + bool alpha = ColorComponentsClose(SkColorGetA(color1), SkColorGetA(color2), + color_type); + if (color_type == kRGB_565_SkColorType) { + return red && blue && green; + } + return red && blue && green && alpha; + } + + bool IsEqual(const SkBitmap& bmp1, const SkBitmap& bmp2) { + if (bmp1.isNull() && bmp2.isNull()) + return true; + if (bmp1.width() != bmp2.width() || bmp1.height() != bmp2.height()) { + LOG(ERROR) << "Bitmap geometry check failure"; + return false; + } + if (bmp1.colorType() != bmp2.colorType()) + return false; + + SkAutoLockPixels lock1(bmp1); + SkAutoLockPixels lock2(bmp2); + if (!bmp1.getPixels() || !bmp2.getPixels()) { + LOG(ERROR) << "Empty Bitmap!"; + return false; + } + for (int y = 0; y < bmp1.height(); ++y) { + for (int x = 0; x < bmp1.width(); ++x) { + if (!ColorsClose(bmp1.getColor(x, y), bmp2.getColor(x, y), + bmp1.colorType())) { + LOG(ERROR) << "Bitmap color comparision failure"; + return false; + } + } + } + return true; + } + + void BindAndAttachTextureWithPixels(GLuint src_texture, + SkColorType color_type, + const gfx::Size& src_size, + const SkBitmap& input_pixels) { + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + GLenum format = 0; + switch (color_type) { + case kBGRA_8888_SkColorType: + format = GL_BGRA_EXT; + break; + case kRGBA_8888_SkColorType: + format = GL_RGBA; + break; + case kRGB_565_SkColorType: + format = GL_RGB; + break; + default: + NOTREACHED(); + } + GLenum type = (color_type == kRGB_565_SkColorType) ? + GL_UNSIGNED_SHORT_5_6_5 : GL_UNSIGNED_BYTE; + gl_->TexImage2D(GL_TEXTURE_2D, 0, format, src_size.width(), + src_size.height(), 0, format, type, + input_pixels.getPixels()); + } + + void ReadBackTexture(GLuint src_texture, + const gfx::Size& src_size, + unsigned char* pixels, + SkColorType color_type, + bool async) { + if (async) { + base::RunLoop run_loop; + helper_->ReadbackTextureAsync( + src_texture, src_size, pixels, color_type, + base::Bind(&callcallback, run_loop.QuitClosure())); + run_loop.Run(); + } else { + helper_->ReadbackTextureSync(src_texture, gfx::Rect(src_size), pixels, + color_type); + } + } + // Test basic format readback. + bool TestTextureFormatReadback(const gfx::Size& src_size, + SkColorType color_type, + bool async) { + SkImageInfo info = SkImageInfo::Make(src_size.width(), src_size.height(), + color_type, kPremul_SkAlphaType); + if (!helper_->IsReadbackConfigSupported(color_type)) { + LOG(INFO) << "Skipping test format not supported" << color_type; + return true; + } + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + SkBitmap input_pixels; + input_pixels.allocPixels(info); + // Test Pattern-1, Fill with Plain color pattern. + // Erase the input bitmap with red color. + input_pixels.eraseColor(SK_ColorRED); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + SkBitmap output_pixels; + output_pixels.allocPixels(info); + // Initialize the output bitmap with Green color. + // When the readback is over output bitmap should have the red color. + output_pixels.eraseColor(SK_ColorGREEN); + uint8_t* pixels = static_cast(output_pixels.getPixels()); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + bool result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-1"; + return false; + } + const int rect_w = 10, rect_h = 4, src_grid_pitch = 10, src_grid_width = 4; + const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE; + // Test Pattern-2, Fill with Grid Pattern. + DrawGridToBitmap(src_size.width(), src_size.height(), color2, color1, + src_grid_pitch, src_grid_width, input_pixels); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-2"; + return false; + } + // Test Pattern-3, Fill with CheckerBoard Pattern. + DrawCheckerToBitmap(src_size.width(), src_size.height(), color1, color2, + rect_w, rect_h, input_pixels); + BindAndAttachTextureWithPixels(src_texture, color_type, src_size, + input_pixels); + ReadBackTexture(src_texture, src_size, pixels, color_type, async); + result = IsEqual(input_pixels, output_pixels); + if (!result) { + LOG(ERROR) << "Bitmap comparision failure Pattern-3"; + return false; + } + gl_->DeleteTextures(1, &src_texture); + if (HasFailure()) { + return false; + } + return true; + } + + // YUV readback test. Create a test pattern, convert to YUV + // with reference implementation and compare to what gl_helper + // returns. + void TestYUVReadback(int xsize, + int ysize, + int output_xsize, + int output_ysize, + int xmargin, + int ymargin, + int test_pattern, + bool flip, + bool use_mrt, + content::GLHelper::ScalerQuality quality) { + GLuint src_texture; + gl_->GenTextures(1, &src_texture); + SkBitmap input_pixels; + input_pixels.allocN32Pixels(xsize, ysize); + + for (int x = 0; x < xsize; ++x) { + for (int y = 0; y < ysize; ++y) { + switch (test_pattern) { + case 0: // Smooth test pattern + SetChannel(&input_pixels, x, y, 0, x * 10); + SetChannel(&input_pixels, x, y, 1, y * 10); + SetChannel(&input_pixels, x, y, 2, (x + y) * 10); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 1: // Small blocks + SetChannel(&input_pixels, x, y, 0, x & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 1, y & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 2, (x + y) & 1 ? 255 : 0); + SetChannel(&input_pixels, x, y, 3, 255); + break; + case 2: // Medium blocks + SetChannel(&input_pixels, x, y, 0, 10 + x / 2 * 50); + SetChannel(&input_pixels, x, y, 1, 10 + y / 3 * 50); + SetChannel(&input_pixels, x, y, 2, (x + y) / 5 * 50 + 5); + SetChannel(&input_pixels, x, y, 3, 255); + break; + } + } + } + + gl_->BindTexture(GL_TEXTURE_2D, src_texture); + gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsize, ysize, 0, GL_RGBA, + GL_UNSIGNED_BYTE, input_pixels.getPixels()); + + gpu::Mailbox mailbox; + gl_->GenMailboxCHROMIUM(mailbox.name); + EXPECT_FALSE(mailbox.IsZero()); + gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); + const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM(); + gl_->ShallowFlushCHROMIUM(); + + gpu::SyncToken sync_token; + gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); + + std::string message = base::StringPrintf( + "input size: %dx%d " + "output size: %dx%d " + "margin: %dx%d " + "pattern: %d %s %s", + xsize, ysize, output_xsize, output_ysize, xmargin, ymargin, + test_pattern, flip ? "flip" : "noflip", flip ? "mrt" : "nomrt"); + scoped_ptr yuv_reader( + helper_->CreateReadbackPipelineYUV( + quality, gfx::Size(xsize, ysize), gfx::Rect(0, 0, xsize, ysize), + gfx::Size(xsize, ysize), flip, use_mrt)); + + scoped_refptr output_frame = + media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, + // The coded size of the output frame is rounded up to the next + // 16-byte boundary. This tests that the readback is being + // positioned inside the frame's visible region, and not dependent + // on its coded size. + gfx::Size((output_xsize + 15) & ~15, (output_ysize + 15) & ~15), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + scoped_refptr truth_frame = + media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, gfx::Size(output_xsize, output_ysize), + gfx::Rect(0, 0, output_xsize, output_ysize), + gfx::Size(output_xsize, output_ysize), + base::TimeDelta::FromSeconds(0)); + + base::RunLoop run_loop; + yuv_reader->ReadbackYUV(mailbox, sync_token, output_frame.get(), + gfx::Point(xmargin, ymargin), + base::Bind(&callcallback, run_loop.QuitClosure())); + run_loop.Run(); + + if (flip) { + FlipSKBitmap(&input_pixels); + } + + unsigned char* Y = truth_frame->visible_data(media::VideoFrame::kYPlane); + unsigned char* U = truth_frame->visible_data(media::VideoFrame::kUPlane); + unsigned char* V = truth_frame->visible_data(media::VideoFrame::kVPlane); + int32_t y_stride = truth_frame->stride(media::VideoFrame::kYPlane); + int32_t u_stride = truth_frame->stride(media::VideoFrame::kUPlane); + int32_t v_stride = truth_frame->stride(media::VideoFrame::kVPlane); + memset(Y, 0x00, y_stride * output_ysize); + memset(U, 0x80, u_stride * output_ysize / 2); + memset(V, 0x80, v_stride * output_ysize / 2); + + const float kRGBtoYColorWeights[] = {0.257f, 0.504f, 0.098f, 0.0625f}; + const float kRGBtoUColorWeights[] = {-0.148f, -0.291f, 0.439f, 0.5f}; + const float kRGBtoVColorWeights[] = {0.439f, -0.368f, -0.071f, 0.5f}; + + for (int y = 0; y < ysize; y++) { + for (int x = 0; x < xsize; x++) { + Y[(y + ymargin) * y_stride + x + xmargin] = float_to_byte( + ChannelAsFloat(&input_pixels, x, y, 0) * kRGBtoYColorWeights[0] + + ChannelAsFloat(&input_pixels, x, y, 1) * kRGBtoYColorWeights[1] + + ChannelAsFloat(&input_pixels, x, y, 2) * kRGBtoYColorWeights[2] + + kRGBtoYColorWeights[3]); + } + } + + for (int y = 0; y < ysize / 2; y++) { + for (int x = 0; x < xsize / 2; x++) { + U[(y + ymargin / 2) * u_stride + x + xmargin / 2] = + float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * + kRGBtoUColorWeights[0] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * + kRGBtoUColorWeights[1] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * + kRGBtoUColorWeights[2] + + kRGBtoUColorWeights[3]); + V[(y + ymargin / 2) * v_stride + x + xmargin / 2] = + float_to_byte(Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 0) * + kRGBtoVColorWeights[0] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 1) * + kRGBtoVColorWeights[1] + + Bilinear(&input_pixels, x * 2 + 1.0, y * 2 + 1.0, 2) * + kRGBtoVColorWeights[2] + + kRGBtoVColorWeights[3]); + } + } + + ComparePlane( + Y, y_stride, output_frame->visible_data(media::VideoFrame::kYPlane), + output_frame->stride(media::VideoFrame::kYPlane), 2, output_xsize, + output_ysize, &input_pixels, message + " Y plane"); + ComparePlane( + U, u_stride, output_frame->visible_data(media::VideoFrame::kUPlane), + output_frame->stride(media::VideoFrame::kUPlane), 2, output_xsize / 2, + output_ysize / 2, &input_pixels, message + " U plane"); + ComparePlane( + V, v_stride, output_frame->visible_data(media::VideoFrame::kVPlane), + output_frame->stride(media::VideoFrame::kVPlane), 2, output_xsize / 2, + output_ysize / 2, &input_pixels, message + " V plane"); + + gl_->DeleteTextures(1, &src_texture); + } + + void TestAddOps(int src, int dst, bool scale_x, bool allow3) { + std::deque ops; + GLHelperScaling::ScaleOp::AddOps(src, dst, scale_x, allow3, &ops); + // Scale factor 3 is a special case. + // It is currently only allowed by itself. + if (allow3 && dst * 3 >= src && dst * 2 < src) { + EXPECT_EQ(ops[0].scale_factor, 3); + EXPECT_EQ(ops.size(), 1U); + EXPECT_EQ(ops[0].scale_x, scale_x); + EXPECT_EQ(ops[0].scale_size, dst); + return; + } + + for (size_t i = 0; i < ops.size(); i++) { + EXPECT_EQ(ops[i].scale_x, scale_x); + if (i == 0) { + // Only the first op is allowed to be a scale up. + // (Scaling up *after* scaling down would make it fuzzy.) + EXPECT_TRUE(ops[0].scale_factor == 0 || ops[0].scale_factor == 2); + } else { + // All other operations must be 50% downscales. + EXPECT_EQ(ops[i].scale_factor, 2); + } + } + // Check that the scale factors make sense and add up. + int tmp = dst; + for (int i = static_cast(ops.size() - 1); i >= 0; i--) { + EXPECT_EQ(tmp, ops[i].scale_size); + if (ops[i].scale_factor == 0) { + EXPECT_EQ(i, 0); + EXPECT_GT(tmp, src); + tmp = src; + } else { + tmp *= ops[i].scale_factor; + } + } + EXPECT_EQ(tmp, src); + } + + void CheckPipeline2(int xsize, + int ysize, + int dst_xsize, + int dst_ysize, + const std::string& description) { + std::vector stages; + helper_scaling_->ConvertScalerOpsToScalerStages( + content::GLHelper::SCALER_QUALITY_GOOD, gfx::Size(xsize, ysize), + gfx::Rect(0, 0, xsize, ysize), gfx::Size(dst_xsize, dst_ysize), false, + false, &x_ops_, &y_ops_, &stages); + EXPECT_EQ(x_ops_.size(), 0U); + EXPECT_EQ(y_ops_.size(), 0U); + ValidateScalerStages(content::GLHelper::SCALER_QUALITY_GOOD, stages, + gfx::Size(dst_xsize, dst_ysize), ""); + EXPECT_EQ(PrintStages(stages), description); + } + + void CheckOptimizationsTest() { + // Basic upscale. X and Y should be combined into one pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); + CheckPipeline2(1024, 768, 2000, 2000, "1024x768 -> 2000x2000 bilinear\n"); + + // X scaled 1/2, Y upscaled, should still be one pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 2000)); + CheckPipeline2(1024, 768, 512, 2000, "1024x768 -> 512x2000 bilinear\n"); + + // X upscaled, Y scaled 1/2, one bilinear pass + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 2000)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); + CheckPipeline2(1024, 768, 2000, 384, "1024x768 -> 2000x384 bilinear\n"); + + // X scaled 1/2, Y scaled 1/2, one bilinear pass + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 512)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 384)); + CheckPipeline2(1024, 768, 512, 384, "1024x768 -> 512x384 bilinear\n"); + + // X scaled 1/2, Y scaled to 60%, one bilinear2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 50, 60, "100x100 -> 50x60 bilinear2 Y\n"); + + // X scaled to 60%, Y scaled 1/2, one bilinear2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 50)); + CheckPipeline2(100, 100, 60, 50, "100x100 -> 60x50 bilinear2 X\n"); + + // X scaled to 60%, Y scaled 60%, one bilinear2x2 pass. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 60, 60, "100x100 -> 60x60 bilinear2x2\n"); + + // X scaled to 40%, Y scaled 40%, two bilinear3 passes. + x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); + y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); + CheckPipeline2(100, 100, 40, 40, + "100x100 -> 100x40 bilinear3 Y\n" + "100x40 -> 40x40 bilinear3 X\n"); + + // X scaled to 60%, Y scaled 40% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(3, false, 40)); + CheckPipeline2(100, 100, 60, 40, + "100x100 -> 100x40 bilinear3 Y\n" + "100x40 -> 60x40 bilinear2 X\n"); + + // X scaled to 40%, Y scaled 60% + x_ops_.push_back(GLHelperScaling::ScaleOp(3, true, 40)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + CheckPipeline2(100, 100, 40, 60, + "100x100 -> 100x60 bilinear2 Y\n" + "100x60 -> 40x60 bilinear3 X\n"); + + // X scaled to 30%, Y scaled 30% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 120)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 60)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 30)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 30, 30, + "100x100 -> 100x30 bilinear4 Y\n" + "100x30 -> 30x30 bilinear4 X\n"); + + // X scaled to 50%, Y scaled 30% + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 50)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 50, 30, "100x100 -> 50x30 bilinear4 Y\n"); + + // X scaled to 150%, Y scaled 30% + // Note that we avoid combinding X and Y passes + // as that would probably be LESS efficient here. + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 150)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 120)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 60)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 30)); + CheckPipeline2(100, 100, 150, 30, + "100x100 -> 100x30 bilinear4 Y\n" + "100x30 -> 150x30 bilinear\n"); + + // X scaled to 1%, Y scaled 1% + x_ops_.push_back(GLHelperScaling::ScaleOp(0, true, 128)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 64)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 32)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 16)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 8)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 4)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 2)); + x_ops_.push_back(GLHelperScaling::ScaleOp(2, true, 1)); + y_ops_.push_back(GLHelperScaling::ScaleOp(0, false, 128)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 64)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 32)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 16)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 8)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 4)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 2)); + y_ops_.push_back(GLHelperScaling::ScaleOp(2, false, 1)); + CheckPipeline2(100, 100, 1, 1, + "100x100 -> 100x32 bilinear4 Y\n" + "100x32 -> 100x4 bilinear4 Y\n" + "100x4 -> 64x1 bilinear2x2\n" + "64x1 -> 8x1 bilinear4 X\n" + "8x1 -> 1x1 bilinear4 X\n"); + } + + scoped_ptr context_; + gpu::gles2::GLES2Interface* gl_; + scoped_ptr helper_; + scoped_ptr helper_scaling_; + std::deque x_ops_, y_ops_; +}; + +class GLHelperPixelTest : public GLHelperTest { + private: + gfx::DisableNullDrawGLBindings enable_pixel_output_; +}; + +TEST_F(GLHelperTest, RGBASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGBA_8888_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, BGRASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kBGRA_8888_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGB565SyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGB_565_SkColorType, false); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGBAASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGBA_8888_SkColorType, true); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, BGRAASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kBGRA_8888_SkColorType, true); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperTest, RGB565ASyncReadbackTest) { + const int kTestSize = 64; + bool result = TestTextureFormatReadback(gfx::Size(kTestSize, kTestSize), + kRGB_565_SkColorType, true); + EXPECT_EQ(result, true); +} + +TEST_F(GLHelperPixelTest, YUVReadbackOptTest) { + // This test uses the gpu.service/gpu_decoder tracing events to detect how + // many scaling passes are actually performed by the YUV readback pipeline. + StartTracing(TRACE_DISABLED_BY_DEFAULT( + "gpu.service") "," TRACE_DISABLED_BY_DEFAULT("gpu_decoder")); + + TestYUVReadback(800, 400, 800, 400, 0, 0, 1, false, true, + content::GLHelper::SCALER_QUALITY_FAST); + + std::map event_counts; + EndTracing(&event_counts); + int draw_buffer_calls = event_counts["kDrawBuffersEXTImmediate"]; + int draw_arrays_calls = event_counts["kDrawArrays"]; + VLOG(1) << "Draw buffer calls: " << draw_buffer_calls; + VLOG(1) << "DrawArrays calls: " << draw_arrays_calls; + + if (draw_buffer_calls) { + // When using MRT, the YUV readback code should only + // execute two draw arrays, and scaling should be integrated + // into those two calls since we are using the FAST scalign + // quality. + EXPECT_EQ(2, draw_arrays_calls); + } else { + // When not using MRT, there are three passes for the YUV, + // and one for the scaling. + EXPECT_EQ(4, draw_arrays_calls); + } +} + +class GLHelperPixelYuvReadback + : public GLHelperPixelTest, + public ::testing::WithParamInterface< + std::tr1::tuple> {}; + +int kYUVReadBackSizes[] = {2, 4, 14}; + +TEST_P(GLHelperPixelYuvReadback, Test) { + bool flip = std::tr1::get<0>(GetParam()); + bool use_mrt = std::tr1::get<1>(GetParam()); + unsigned int x = std::tr1::get<2>(GetParam()); + unsigned int y = std::tr1::get<3>(GetParam()); + + for (unsigned int ox = x; ox < arraysize(kYUVReadBackSizes); ox++) { + for (unsigned int oy = y; oy < arraysize(kYUVReadBackSizes); oy++) { + // If output is a subsection of the destination frame, (letterbox) + // then try different variations of where the subsection goes. + for (Margin xm = x < ox ? MarginLeft : MarginRight; xm <= MarginRight; + xm = NextMargin(xm)) { + for (Margin ym = y < oy ? MarginLeft : MarginRight; ym <= MarginRight; + ym = NextMargin(ym)) { + for (int pattern = 0; pattern < 3; pattern++) { + TestYUVReadback( + kYUVReadBackSizes[x], kYUVReadBackSizes[y], + kYUVReadBackSizes[ox], kYUVReadBackSizes[oy], + compute_margin(kYUVReadBackSizes[x], kYUVReadBackSizes[ox], xm), + compute_margin(kYUVReadBackSizes[y], kYUVReadBackSizes[oy], ym), + pattern, flip, use_mrt, content::GLHelper::SCALER_QUALITY_GOOD); + if (HasFailure()) { + return; + } + } + } + } + } + } +} + +// First argument is intentionally empty. +INSTANTIATE_TEST_CASE_P( + , + GLHelperPixelYuvReadback, + ::testing::Combine( + ::testing::Bool(), + ::testing::Bool(), + ::testing::Range(0, arraysize(kYUVReadBackSizes)), + ::testing::Range(0, arraysize(kYUVReadBackSizes)))); + +int kRGBReadBackSizes[] = {3, 6, 16}; + +class GLHelperPixelReadbackTest + : public GLHelperPixelTest, + public ::testing::WithParamInterface> {}; + +// Per pixel tests, all sizes are small so that we can print +// out the generated bitmaps. +TEST_P(GLHelperPixelReadbackTest, ScaleTest) { + unsigned int q_index = std::tr1::get<0>(GetParam()); + unsigned int x = std::tr1::get<1>(GetParam()); + unsigned int y = std::tr1::get<2>(GetParam()); + unsigned int dst_x = std::tr1::get<3>(GetParam()); + unsigned int dst_y = std::tr1::get<4>(GetParam()); + + for (int flip = 0; flip <= 1; flip++) { + for (int pattern = 0; pattern < 3; pattern++) { + TestScale(kRGBReadBackSizes[x], kRGBReadBackSizes[y], + kRGBReadBackSizes[dst_x], kRGBReadBackSizes[dst_y], pattern, + q_index, flip == 1); + if (HasFailure()) { + return; + } + } + } +} + +// Per pixel tests, all sizes are small so that we can print +// out the generated bitmaps. +TEST_P(GLHelperPixelReadbackTest, CropScaleReadbackAndCleanTextureTest) { + unsigned int q_index = std::tr1::get<0>(GetParam()); + unsigned int x = std::tr1::get<1>(GetParam()); + unsigned int y = std::tr1::get<2>(GetParam()); + unsigned int dst_x = std::tr1::get<3>(GetParam()); + unsigned int dst_y = std::tr1::get<4>(GetParam()); + + const SkColorType kColorTypes[] = { + kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType}; + for (size_t color_type = 0; color_type < arraysize(kColorTypes); + color_type++) { + for (int pattern = 0; pattern < 3; pattern++) { + TestCropScaleReadbackAndCleanTexture( + kRGBReadBackSizes[x], kRGBReadBackSizes[y], kRGBReadBackSizes[dst_x], + kRGBReadBackSizes[dst_y], pattern, kColorTypes[color_type], false, + q_index); + if (HasFailure()) + return; + } + } +} + +INSTANTIATE_TEST_CASE_P( + , + GLHelperPixelReadbackTest, + ::testing::Combine( + ::testing::Range(0, arraysize(kQualities)), + ::testing::Range(0, arraysize(kRGBReadBackSizes)), + ::testing::Range(0, arraysize(kRGBReadBackSizes)), + ::testing::Range(0, arraysize(kRGBReadBackSizes)), + ::testing::Range(0, arraysize(kRGBReadBackSizes)))); + +// Validate that all scaling generates valid pipelines. +TEST_F(GLHelperTest, ValidateScalerPipelines) { + int sizes[] = {7, 99, 128, 256, 512, 719, 720, 721, 1920, 2011, 3217, 4096}; + for (size_t q = 0; q < arraysize(kQualities); q++) { + for (size_t x = 0; x < arraysize(sizes); x++) { + for (size_t y = 0; y < arraysize(sizes); y++) { + for (size_t dst_x = 0; dst_x < arraysize(sizes); dst_x++) { + for (size_t dst_y = 0; dst_y < arraysize(sizes); dst_y++) { + TestScalerPipeline(q, sizes[x], sizes[y], sizes[dst_x], + sizes[dst_y]); + if (HasFailure()) { + return; + } + } + } + } + } + } +} + +// Make sure we don't create overly complicated pipelines +// for a few common use cases. +TEST_F(GLHelperTest, CheckSpecificPipelines) { + // Upscale should be single pass. + CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 1024, 700, 1280, 720, + "1024x700 -> 1280x720 bilinear\n"); + // Slight downscale should use BILINEAR2X2. + CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 1280, 720, 1024, 700, + "1280x720 -> 1024x700 bilinear2x2\n"); + // Most common tab capture pipeline on the Pixel. + // Should be using two BILINEAR3 passes. + CheckPipeline(content::GLHelper::SCALER_QUALITY_GOOD, 2560, 1476, 1249, 720, + "2560x1476 -> 2560x720 bilinear3 Y\n" + "2560x720 -> 1249x720 bilinear3 X\n"); +} + +TEST_F(GLHelperTest, ScalerOpTest) { + for (int allow3 = 0; allow3 <= 1; allow3++) { + for (int dst = 1; dst < 2049; dst += 1 + (dst >> 3)) { + for (int src = 1; src < 2049; src++) { + TestAddOps(src, dst, allow3 == 1, (src & 1) == 1); + if (HasFailure()) { + LOG(ERROR) << "Failed for src=" << src << " dst=" << dst + << " allow3=" << allow3; + return; + } + } + } + } +} + +TEST_F(GLHelperTest, CheckOptimizations) { + // Test in baseclass since it is friends with GLHelperScaling + CheckOptimizationsTest(); +} + +} // namespace content diff --git a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc index adf0764c65a..f3eb5f5aa20 100644 --- a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc @@ -41,12 +41,12 @@ GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface( GpuBrowserCompositorOutputSurface::~GpuBrowserCompositorOutputSurface() {} -CommandBufferProxyImpl* +gpu::CommandBufferProxyImpl* GpuBrowserCompositorOutputSurface::GetCommandBufferProxy() { ContextProviderCommandBuffer* provider_command_buffer = static_cast( context_provider_.get()); - CommandBufferProxyImpl* command_buffer_proxy = + gpu::CommandBufferProxyImpl* command_buffer_proxy = provider_command_buffer->GetCommandBufferProxy(); DCHECK(command_buffer_proxy); return command_buffer_proxy; diff --git a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h index ae08e8d1a2d..4133bc36951 100644 --- a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h @@ -11,12 +11,15 @@ #include "content/browser/compositor/browser_compositor_output_surface.h" #include "ui/gfx/swap_result.h" +namespace gpu { +class CommandBufferProxyImpl; +} + namespace ui { class CompositorVSyncManager; } namespace content { -class CommandBufferProxyImpl; class BrowserCompositorOverlayCandidateValidator; class ReflectorTexture; @@ -64,7 +67,7 @@ class GpuBrowserCompositorOutputSurface ShouldShowFramesState should_show_frames_state_; #endif - CommandBufferProxyImpl* GetCommandBufferProxy(); + gpu::CommandBufferProxyImpl* GetCommandBufferProxy(); base::CancelableCallback&, gfx::SwapResult)> diff --git a/chromium/content/browser/compositor/gpu_process_transport_factory.cc b/chromium/content/browser/compositor/gpu_process_transport_factory.cc index 874debf5f1e..9a4ad655d5b 100644 --- a/chromium/content/browser/compositor/gpu_process_transport_factory.cc +++ b/chromium/content/browser/compositor/gpu_process_transport_factory.cc @@ -26,6 +26,7 @@ #include "cc/surfaces/surface_manager.h" #include "content/browser/compositor/browser_compositor_output_surface.h" #include "content/browser/compositor/browser_compositor_overlay_candidate_validator.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/gpu_browser_compositor_output_surface.h" #include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h" #include "content/browser/compositor/offscreen_browser_compositor_output_surface.h" @@ -34,27 +35,24 @@ #include "content/browser/compositor/software_output_device_mus.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" -#include "content/browser/gpu/compositor_util.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gl_helper.h" -#include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" -#include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/common/gpu_process_launch_causes.h" #include "content/common/host_shared_bitmap_manager.h" #include "content/public/common/content_switches.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/common/mailbox.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "third_party/khronos/GLES2/gl2.h" #include "ui/compositor/compositor.h" #include "ui/compositor/compositor_constants.h" #include "ui/compositor/compositor_switches.h" #include "ui/compositor/layer.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/native_widget_types.h" #if defined(MOJO_RUNNER_CLIENT) #include "content/common/mojo/mojo_shell_connection_impl.h" @@ -62,6 +60,7 @@ #if defined(OS_WIN) #include "content/browser/compositor/software_output_device_win.h" +#include "ui/gfx/win/rendering_window_manager.h" #elif defined(USE_OZONE) #include "content/browser/compositor/browser_compositor_overlay_candidate_validator_ozone.h" #include "content/browser/compositor/software_output_device_ozone.h" @@ -99,11 +98,9 @@ GpuProcessTransportFactory::GpuProcessTransportFactory() : next_surface_id_namespace_(1u), task_graph_runner_(new cc::SingleThreadTaskGraphRunner), callback_factory_(this) { - ui::Layer::InitializeUILayerSettings(); cc::SetClientNameForMetrics("Browser"); - if (UseSurfacesEnabled()) - surface_manager_ = make_scoped_ptr(new cc::SurfaceManager); + surface_manager_ = make_scoped_ptr(new cc::SurfaceManager); task_graph_runner_->Start("CompositorTileWorker1", base::SimpleThread::Options()); @@ -123,15 +120,11 @@ GpuProcessTransportFactory::~GpuProcessTransportFactory() { scoped_ptr GpuProcessTransportFactory::CreateOffscreenCommandBufferContext() { -#if defined(OS_ANDROID) - return CreateContextCommon(scoped_refptr(nullptr), 0); -#else CauseForGpuLaunch cause = CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE; - scoped_refptr gpu_channel_host( + scoped_refptr gpu_channel_host( BrowserGpuChannelHostFactory::instance()->EstablishGpuChannelSync(cause)); - return CreateContextCommon(gpu_channel_host, 0); -#endif // OS_ANDROID + return CreateContextCommon(gpu_channel_host, gpu::kNullSurfaceHandle); } scoped_ptr @@ -224,6 +217,11 @@ void GpuProcessTransportFactory::CreateOutputSurface( data->surface = nullptr; } +#if defined(OS_WIN) + gfx::RenderingWindowManager::GetInstance()->UnregisterParent( + compositor->widget()); +#endif + bool create_gpu_output_surface = ShouldCreateGpuOutputSurface(compositor.get()); if (create_gpu_output_surface) { @@ -262,6 +260,11 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( create_gpu_output_surface = false; } +#if defined(OS_WIN) + gfx::RenderingWindowManager::GetInstance()->RegisterParent( + compositor->widget()); +#endif + scoped_refptr context_provider; if (create_gpu_output_surface) { // Try to reuse existing worker context provider. @@ -274,20 +277,26 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( shared_worker_context_provider_lost = true; } } - scoped_refptr gpu_channel_host = + scoped_refptr gpu_channel_host = BrowserGpuChannelHostFactory::instance()->GetGpuChannel(); if (gpu_channel_host.get()) { + GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); + gpu::SurfaceHandle surface_handle = + data->surface_id ? tracker->GetSurfaceHandle(data->surface_id) + : gpu::kNullSurfaceHandle; + // This context is used for both the browser compositor and the display + // compositor. context_provider = ContextProviderCommandBuffer::Create( GpuProcessTransportFactory::CreateContextCommon(gpu_channel_host, - data->surface_id), - BROWSER_COMPOSITOR_ONSCREEN_CONTEXT); + surface_handle), + DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT); if (context_provider && !context_provider->BindToCurrentThread()) context_provider = nullptr; if (!shared_worker_context_provider_ || shared_worker_context_provider_lost) { shared_worker_context_provider_ = ContextProviderCommandBuffer::Create( - GpuProcessTransportFactory::CreateContextCommon(gpu_channel_host, - 0), + GpuProcessTransportFactory::CreateContextCommon( + gpu_channel_host, gpu::kNullSurfaceHandle), BROWSER_WORKER_CONTEXT); if (shared_worker_context_provider_ && !shared_worker_context_provider_->BindToCurrentThread()) @@ -334,7 +343,7 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( GLenum format = GL_RGB; #if defined(OS_MACOSX) target = GL_TEXTURE_RECTANGLE_ARB; - format = GL_BGRA_EXT; + format = GL_RGBA; #endif surface = make_scoped_ptr(new GpuSurfacelessBrowserCompositorOutputSurface( @@ -361,10 +370,10 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( if (data->reflector) data->reflector->OnSourceSurfaceReady(data->surface); - if (!UseSurfacesEnabled()) { - compositor->SetOutputSurface(std::move(surface)); - return; - } +#if defined(OS_WIN) + gfx::RenderingWindowManager::GetInstance()->DoSetParentOnChild( + compositor->widget()); +#endif // This gets a bit confusing. Here we have a ContextProvider in the |surface| // configured to render directly to this widget. We need to make an @@ -442,6 +451,10 @@ void GpuProcessTransportFactory::RemoveCompositor(ui::Compositor* compositor) { DCHECK(!gl_helper_) << "Destroying the GLHelper should not cause a new " "GLHelper to be created."; } +#if defined(OS_WIN) + gfx::RenderingWindowManager::GetInstance()->UnregisterParent( + compositor->widget()); +#endif } bool GpuProcessTransportFactory::DoesCreateTestContexts() { return false; } @@ -579,24 +592,13 @@ GpuProcessTransportFactory::CreatePerCompositorData( DCHECK(!per_compositor_data_[compositor]); gfx::AcceleratedWidget widget = compositor->widget(); - GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); PerCompositorData* data = new PerCompositorData; if (compositor->widget() == gfx::kNullAcceleratedWidget) { data->surface_id = 0; } else { + GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); data->surface_id = tracker->AddSurfaceForNativeWidget(widget); -#if defined(OS_MACOSX) || defined(OS_ANDROID) - // On Mac and Android, we can't pass the AcceleratedWidget, which is - // process-local, so instead we pass the surface_id, so that we can look up - // the AcceleratedWidget on the GPU side or when we receive - // GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params. - gfx::PluginWindowHandle handle = data->surface_id; -#else - gfx::PluginWindowHandle handle = widget; -#endif - tracker->SetSurfaceHandle(data->surface_id, - gfx::GLSurfaceHandle(handle, gfx::NATIVE_DIRECT)); } per_compositor_data_[compositor] = data; @@ -606,31 +608,46 @@ GpuProcessTransportFactory::CreatePerCompositorData( scoped_ptr GpuProcessTransportFactory::CreateContextCommon( - scoped_refptr gpu_channel_host, - int surface_id) { + scoped_refptr gpu_channel_host, + gpu::SurfaceHandle surface_handle) { if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) - return scoped_ptr(); - blink::WebGraphicsContext3D::Attributes attrs; - attrs.shareResources = true; - attrs.depth = false; - attrs.stencil = false; - attrs.antialias = false; - attrs.noAutomaticFlushes = true; - bool lose_context_when_out_of_memory = true; - if (!gpu_channel_host.get()) { + return nullptr; + if (!gpu_channel_host) { LOG(ERROR) << "Failed to establish GPU channel."; - return scoped_ptr(); + return nullptr; } + + // This is called from a few places to create different contexts: + // - The shared main thread context (offscreen). + // - The compositor context, which is used by the browser compositor + // (offscreen) for synchronization mostly, and by the display compositor + // (onscreen) for actual GL drawing. + // - The compositor worker context (offscreen) used for GPU raster. + // So ask for capabilities needed by any of these cases (we can optimize by + // branching on |surface_handle| being null if these needs diverge). + // + // The default framebuffer for an offscreen context is not used, so it does + // not need alpha, stencil, depth, antialiasing. The display compositor does + // not use these things either, so we can request nothing here. + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 0; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.bind_generates_resource = false; + attributes.lose_context_when_out_of_memory = true; + + bool share_resources = true; + bool automatic_flushes = false; + GURL url("chrome://gpu/GpuProcessTransportFactory::CreateContextCommon"); scoped_ptr context( new WebGraphicsContext3DCommandBufferImpl( - surface_id, - url, - gpu_channel_host.get(), - attrs, - lose_context_when_out_of_memory, + surface_handle, url, gpu_channel_host.get(), attributes, + gfx::PreferIntegratedGpu, share_resources, automatic_flushes, WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), - NULL)); + nullptr)); return context; } diff --git a/chromium/content/browser/compositor/gpu_process_transport_factory.h b/chromium/content/browser/compositor/gpu_process_transport_factory.h index ee8d60e9ac9..f4e007aa188 100644 --- a/chromium/content/browser/compositor/gpu_process_transport_factory.h +++ b/chromium/content/browser/compositor/gpu_process_transport_factory.h @@ -17,7 +17,8 @@ #include "base/observer_list.h" #include "build/build_config.h" #include "content/browser/compositor/image_transport_factory.h" -#include "content/common/gpu/client/gpu_channel_host.h" +#include "gpu/ipc/client/gpu_channel_host.h" +#include "gpu/ipc/common/surface_handle.h" #include "ui/compositor/compositor.h" namespace base { @@ -94,8 +95,8 @@ class GpuProcessTransportFactory bool create_gpu_output_surface, int num_attempts); scoped_ptr CreateContextCommon( - scoped_refptr gpu_channel_host, - int surface_id); + scoped_refptr gpu_channel_host, + gpu::SurfaceHandle surface_handle); void OnLostMainThreadSharedContextInsideCallback(); void OnLostMainThreadSharedContext(); diff --git a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc index 0913548f2fa..5e85a4e315d 100644 --- a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc @@ -10,10 +10,10 @@ #include "cc/output/output_surface_client.h" #include "content/browser/compositor/browser_compositor_overlay_candidate_validator.h" #include "content/browser/compositor/buffer_queue.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/reflector_impl.h" #include "content/browser/gpu/gpu_surface_tracker.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gl_helper.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -29,7 +29,7 @@ GpuSurfacelessBrowserCompositorOutputSurface:: overlay_candidate_validator, unsigned int target, unsigned int internalformat, - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager) + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) : GpuBrowserCompositorOutputSurface(context, worker_context, vsync_manager, diff --git a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h index a869fccd8cf..23412198d0e 100644 --- a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h @@ -7,9 +7,12 @@ #include "content/browser/compositor/gpu_browser_compositor_output_surface.h" +namespace gpu { +class GpuMemoryBufferManager; +} + namespace content { -class BrowserGpuMemoryBufferManager; class BufferQueue; class GLHelper; @@ -25,7 +28,7 @@ class GpuSurfacelessBrowserCompositorOutputSurface overlay_candidate_validator, unsigned int target, unsigned int internalformat, - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager); + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager); ~GpuSurfacelessBrowserCompositorOutputSurface() override; private: @@ -45,7 +48,7 @@ class GpuSurfacelessBrowserCompositorOutputSurface unsigned int internalformat_; scoped_ptr gl_helper_; scoped_ptr output_surface_; - BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager_; + gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_; }; } // namespace content diff --git a/chromium/content/browser/compositor/owned_mailbox.cc b/chromium/content/browser/compositor/owned_mailbox.cc index 912d4d569b7..8e0558212a6 100644 --- a/chromium/content/browser/compositor/owned_mailbox.cc +++ b/chromium/content/browser/compositor/owned_mailbox.cc @@ -5,8 +5,8 @@ #include "content/browser/compositor/owned_mailbox.h" #include "base/logging.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/image_transport_factory.h" -#include "content/common/gpu/client/gl_helper.h" namespace content { diff --git a/chromium/content/browser/compositor/reflector_texture.cc b/chromium/content/browser/compositor/reflector_texture.cc index 231066275d9..c9b742d425f 100644 --- a/chromium/content/browser/compositor/reflector_texture.cc +++ b/chromium/content/browser/compositor/reflector_texture.cc @@ -4,9 +4,9 @@ #include "content/browser/compositor/reflector_texture.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/compositor/owned_mailbox.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gl_helper.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" diff --git a/chromium/content/browser/compositor/resize_lock.cc b/chromium/content/browser/compositor/resize_lock.cc deleted file mode 100644 index ddbc4437872..00000000000 --- a/chromium/content/browser/compositor/resize_lock.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/compositor/resize_lock.h" - -namespace content { - -ResizeLock::ResizeLock(const gfx::Size new_size, bool defer_compositor_lock) - : new_size_(new_size), - defer_compositor_lock_(defer_compositor_lock) { - if (!defer_compositor_lock_) - LockCompositor(); -} - -ResizeLock::~ResizeLock() { - UnlockCompositor(); -} - -bool ResizeLock::GrabDeferredLock() { - if (!defer_compositor_lock_) - return false; - LockCompositor(); - return true; -} - -void ResizeLock::UnlockCompositor() { - defer_compositor_lock_ = false; -} - -void ResizeLock::LockCompositor() { - defer_compositor_lock_ = false; -} - -} // namespace content diff --git a/chromium/content/browser/compositor/resize_lock.h b/chromium/content/browser/compositor/resize_lock.h deleted file mode 100644 index 8b4829785eb..00000000000 --- a/chromium/content/browser/compositor/resize_lock.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_ -#define CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_ - -#include "base/macros.h" -#include "content/common/content_export.h" -#include "ui/gfx/geometry/size.h" - -namespace content { - -class CONTENT_EXPORT ResizeLock { - public: - virtual ~ResizeLock(); - - virtual bool GrabDeferredLock(); - virtual void UnlockCompositor(); - - const gfx::Size& expected_size() const { return new_size_; } - - protected: - ResizeLock(const gfx::Size new_size, bool defer_compositor_lock); - - virtual void LockCompositor(); - - private: - gfx::Size new_size_; - bool defer_compositor_lock_; - - DISALLOW_COPY_AND_ASSIGN(ResizeLock); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_COMPOSITOR_RESIZE_LOCK_H_ diff --git a/chromium/content/browser/compositor/software_output_device_mac.h b/chromium/content/browser/compositor/software_output_device_mac.h index a613ab4b1d8..84e09873597 100644 --- a/chromium/content/browser/compositor/software_output_device_mac.h +++ b/chromium/content/browser/compositor/software_output_device_mac.h @@ -10,6 +10,8 @@ #include "base/mac/scoped_cftyperef.h" #include "base/macros.h" #include "cc/output/software_output_device.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/vsync_provider.h" @@ -63,7 +65,7 @@ class SoftwareOutputDeviceMac : // The SkCanvas wrapps the mapped current IOSurface. It is valid only between // BeginPaint and EndPaint. - skia::RefPtr canvas_; + sk_sp canvas_; gfx::VSyncProvider::UpdateVSyncCallback update_vsync_callback_; diff --git a/chromium/content/browser/compositor/software_output_device_mac.mm b/chromium/content/browser/compositor/software_output_device_mac.mm index e71f6782878..9c9c3931480 100644 --- a/chromium/content/browser/compositor/software_output_device_mac.mm +++ b/chromium/content/browser/compositor/software_output_device_mac.mm @@ -123,7 +123,7 @@ SkCanvas* SoftwareOutputDeviceMac::BeginPaint( IOSurfaceGetBaseAddress(io_surfaces_[current_index_])); size_t stride = IOSurfaceGetBytesPerRow(io_surfaces_[current_index_]); - canvas_ = skia::AdoptRef(SkCanvas::NewRasterDirectN32( + canvas_ = sk_sp(SkCanvas::NewRasterDirectN32( pixel_size_.width(), pixel_size_.height(), pixels, stride)); CopyPreviousBufferDamage(SkRegion(gfx::RectToSkIRect(new_damage_rect))); @@ -141,7 +141,7 @@ void SoftwareOutputDeviceMac::EndPaint() { DLOG(ERROR) << "Failed to unlock IOSurface " << io_result; } - canvas_ = nullptr; + canvas_.reset(); base::TimeTicks vsync_timebase; base::TimeDelta vsync_interval; ui::AcceleratedWidgetMacGotFrame( diff --git a/chromium/content/browser/compositor/software_output_device_win.cc b/chromium/content/browser/compositor/software_output_device_win.cc index a712def1911..3ab7677a543 100644 --- a/chromium/content/browser/compositor/software_output_device_win.cc +++ b/chromium/content/browser/compositor/software_output_device_win.cc @@ -122,7 +122,7 @@ void SoftwareOutputDeviceWin::Resize(const gfx::Size& viewport_pixel_size, viewport_pixel_size_ = viewport_pixel_size; if (backing_) backing_->Resized(); - contents_.clear(); + contents_.reset(); } SkCanvas* SoftwareOutputDeviceWin::BeginPaint(const gfx::Rect& damage_rect) { @@ -141,7 +141,7 @@ SkCanvas* SoftwareOutputDeviceWin::BeginPaint(const gfx::Rect& damage_rect) { } } if (can_create_contents) { - contents_ = skia::AdoptRef(skia::CreatePlatformCanvas( + contents_ = sk_sp(skia::CreatePlatformCanvas( viewport_pixel_size_.width(), viewport_pixel_size_.height(), true, shared_section, skia::CRASH_ON_FAILURE)); } @@ -180,10 +180,10 @@ void SoftwareOutputDeviceWin::EndPaint() { style |= WS_EX_LAYERED; SetWindowLong(hwnd_, GWL_EXSTYLE, style); - HDC dib_dc = skia::BeginPlatformPaint(contents_.get()); + skia::ScopedPlatformPaint spp(contents_.get()); + HDC dib_dc = spp.GetPlatformSurface(); ::UpdateLayeredWindow(hwnd_, NULL, &position, &size, dib_dc, &zero, RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); - skia::EndPlatformPaint(contents_.get()); } else { HDC hdc = ::GetDC(hwnd_); RECT src_rect = rect.ToRECT(); @@ -196,7 +196,7 @@ void SoftwareOutputDeviceWin::EndPaint() { void SoftwareOutputDeviceWin::ReleaseContents() { DCHECK(!contents_ || contents_->unique()); DCHECK(!in_paint_); - contents_.clear(); + contents_.reset(); } } // namespace content diff --git a/chromium/content/browser/compositor/software_output_device_win.h b/chromium/content/browser/compositor/software_output_device_win.h index 1cc267dd55b..5c80340808c 100644 --- a/chromium/content/browser/compositor/software_output_device_win.h +++ b/chromium/content/browser/compositor/software_output_device_win.h @@ -13,6 +13,8 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "cc/output/software_output_device.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkRefCnt.h" namespace base { class SharedMemory; @@ -61,7 +63,7 @@ class SoftwareOutputDeviceWin : public cc::SoftwareOutputDevice { private: HWND hwnd_; - skia::RefPtr contents_; + sk_sp contents_; bool is_hwnd_composited_; OutputDeviceBacking* backing_; bool in_paint_; diff --git a/chromium/content/browser/compositor/surface_utils.cc b/chromium/content/browser/compositor/surface_utils.cc index 7ae05912a65..4f4b3bad88c 100644 --- a/chromium/content/browser/compositor/surface_utils.cc +++ b/chromium/content/browser/compositor/surface_utils.cc @@ -4,20 +4,162 @@ #include "content/browser/compositor/surface_utils.h" +#include "base/callback_helpers.h" +#include "base/memory/ref_counted.h" #include "build/build_config.h" +#include "cc/output/copy_output_result.h" +#include "cc/resources/single_release_callback.h" #include "cc/surfaces/surface_id_allocator.h" +#include "content/browser/compositor/gl_helper.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/effects/SkLumaColorFilter.h" +#include "ui/gfx/geometry/rect.h" -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) #include "content/browser/renderer_host/compositor_impl_android.h" #else #include "content/browser/compositor/image_transport_factory.h" #include "ui/compositor/compositor.h" #endif +namespace { + +#if !defined(OS_ANDROID) || defined(USE_AURA) +void CopyFromCompositingSurfaceFinished( + const content::ReadbackRequestCallback& callback, + scoped_ptr release_callback, + scoped_ptr bitmap, + scoped_ptr bitmap_pixels_lock, + bool result) { + bitmap_pixels_lock.reset(); + + gpu::SyncToken sync_token; + if (result) { + content::GLHelper* gl_helper = + content::ImageTransportFactory::GetInstance()->GetGLHelper(); + if (gl_helper) + gl_helper->GenerateSyncToken(&sync_token); + } + const bool lost_resource = !sync_token.HasData(); + release_callback->Run(sync_token, lost_resource); + + callback.Run(*bitmap, + result ? content::READBACK_SUCCESS : content::READBACK_FAILED); +} +#endif + +// TODO(wjmaclean): There is significant overlap between +// PrepareTextureCopyOutputResult and CopyFromCompositingSurfaceFinished in +// this file, and the versions in RenderWidgetHostViewAndroid. They should +// be merged. See https://crbug.com/582955 +void PrepareTextureCopyOutputResult( + const gfx::Size& dst_size_in_pixel, + const SkColorType color_type, + const content::ReadbackRequestCallback& callback, + scoped_ptr result) { +#if defined(OS_ANDROID) && !defined(USE_AURA) + // TODO(wjmaclean): See if there's an equivalent pathway for Android and + // implement it here. + callback.Run(SkBitmap(), content::READBACK_FAILED); +#else + DCHECK(result->HasTexture()); + base::ScopedClosureRunner scoped_callback_runner( + base::Bind(callback, SkBitmap(), content::READBACK_FAILED)); + + // TODO(siva.gunturi): We should be able to validate the format here using + // GLHelper::IsReadbackConfigSupported before we processs the result. + // See crbug.com/415682 and crbug.com/415131. + scoped_ptr bitmap(new SkBitmap); + if (!bitmap->tryAllocPixels(SkImageInfo::Make( + dst_size_in_pixel.width(), dst_size_in_pixel.height(), color_type, + kOpaque_SkAlphaType))) { + scoped_callback_runner.Reset(base::Bind( + callback, SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE)); + return; + } + + content::ImageTransportFactory* factory = + content::ImageTransportFactory::GetInstance(); + content::GLHelper* gl_helper = factory->GetGLHelper(); + if (!gl_helper) + return; + + scoped_ptr bitmap_pixels_lock( + new SkAutoLockPixels(*bitmap)); + uint8_t* pixels = static_cast(bitmap->getPixels()); + + cc::TextureMailbox texture_mailbox; + scoped_ptr release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + + ignore_result(scoped_callback_runner.Release()); + + gl_helper->CropScaleReadbackAndCleanMailbox( + texture_mailbox.mailbox(), texture_mailbox.sync_token(), result->size(), + gfx::Rect(result->size()), dst_size_in_pixel, pixels, color_type, + base::Bind(&CopyFromCompositingSurfaceFinished, callback, + base::Passed(&release_callback), base::Passed(&bitmap), + base::Passed(&bitmap_pixels_lock)), + content::GLHelper::SCALER_QUALITY_GOOD); +#endif +} + +void PrepareBitmapCopyOutputResult( + const gfx::Size& dst_size_in_pixel, + const SkColorType preferred_color_type, + const content::ReadbackRequestCallback& callback, + scoped_ptr result) { + SkColorType color_type = preferred_color_type; + if (color_type != kN32_SkColorType && color_type != kAlpha_8_SkColorType) { + // Switch back to default colortype if format not supported. + color_type = kN32_SkColorType; + } + DCHECK(result->HasBitmap()); + scoped_ptr source = result->TakeBitmap(); + DCHECK(source); + SkBitmap scaled_bitmap; + if (source->width() != dst_size_in_pixel.width() || + source->height() != dst_size_in_pixel.height()) { + scaled_bitmap = skia::ImageOperations::Resize( + *source, skia::ImageOperations::RESIZE_BEST, dst_size_in_pixel.width(), + dst_size_in_pixel.height()); + } else { + scaled_bitmap = *source; + } + if (color_type == kN32_SkColorType) { + DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); + callback.Run(scaled_bitmap, content::READBACK_SUCCESS); + return; + } + DCHECK_EQ(color_type, kAlpha_8_SkColorType); + // The software path currently always returns N32 bitmap regardless of the + // |color_type| we ask for. + DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType); + // Paint |scaledBitmap| to alpha-only |grayscale_bitmap|. + SkBitmap grayscale_bitmap; + bool success = grayscale_bitmap.tryAllocPixels( + SkImageInfo::MakeA8(scaled_bitmap.width(), scaled_bitmap.height())); + if (!success) { + callback.Run(SkBitmap(), content::READBACK_BITMAP_ALLOCATION_FAILURE); + return; + } + SkCanvas canvas(grayscale_bitmap); + SkPaint paint; + paint.setColorFilter(SkLumaColorFilter::Make()); + canvas.drawBitmap(scaled_bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint); + callback.Run(grayscale_bitmap, content::READBACK_SUCCESS); +} + +} // namespace + namespace content { scoped_ptr CreateSurfaceIdAllocator() { -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) return CompositorImpl::CreateSurfaceIdAllocator(); #else ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); @@ -26,7 +168,7 @@ scoped_ptr CreateSurfaceIdAllocator() { } cc::SurfaceManager* GetSurfaceManager() { -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) return CompositorImpl::GetSurfaceManager(); #else ImageTransportFactory* factory = ImageTransportFactory::GetInstance(); @@ -34,4 +176,33 @@ cc::SurfaceManager* GetSurfaceManager() { #endif } +void CopyFromCompositingSurfaceHasResult( + const gfx::Size& dst_size_in_pixel, + const SkColorType color_type, + const ReadbackRequestCallback& callback, + scoped_ptr result) { + if (result->IsEmpty() || result->size().IsEmpty()) { + callback.Run(SkBitmap(), READBACK_FAILED); + return; + } + + gfx::Size output_size_in_pixel; + if (dst_size_in_pixel.IsEmpty()) + output_size_in_pixel = result->size(); + else + output_size_in_pixel = dst_size_in_pixel; + + if (result->HasTexture()) { + // GPU-accelerated path + PrepareTextureCopyOutputResult(output_size_in_pixel, color_type, callback, + std::move(result)); + return; + } + + DCHECK(result->HasBitmap()); + // Software path + PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback, + std::move(result)); +} + } // namespace content diff --git a/chromium/content/browser/compositor/surface_utils.h b/chromium/content/browser/compositor/surface_utils.h index b56fef27170..6d986a9c8ca 100644 --- a/chromium/content/browser/compositor/surface_utils.h +++ b/chromium/content/browser/compositor/surface_utils.h @@ -6,8 +6,13 @@ #define CONTENT_BROWSER_COMPOSITOR_SURFACE_UTILS_H_ #include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/public/browser/readback_types.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "ui/gfx/geometry/size.h" namespace cc { +class CopyOutputResult; class SurfaceIdAllocator; class SurfaceManager; } // namespace cc @@ -16,8 +21,15 @@ namespace content { scoped_ptr CreateSurfaceIdAllocator(); +CONTENT_EXPORT cc::SurfaceManager* GetSurfaceManager(); +void CopyFromCompositingSurfaceHasResult( + const gfx::Size& dst_size_in_pixel, + const SkColorType color_type, + const ReadbackRequestCallback& callback, + scoped_ptr result); + } // namespace content #endif // CONTENT_BROWSER_COMPOSITOR_SURFACE_UTILS_H_ diff --git a/chromium/content/browser/cross_site_transfer_browsertest.cc b/chromium/content/browser/cross_site_transfer_browsertest.cc index fbe720d9e11..1b7c099e455 100644 --- a/chromium/content/browser/cross_site_transfer_browsertest.cc +++ b/chromium/content/browser/cross_site_transfer_browsertest.cc @@ -293,7 +293,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, // navigation. This test is the same as the test above, except transfering from // in-process. IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, - ReplaceEntryInProcessThenTranfers) { + ReplaceEntryInProcessThenTransfer) { const NavigationController& controller = shell()->web_contents()->GetController(); @@ -367,8 +367,8 @@ IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, "A.com", "/cross-site/" + url3b.host() + url3b.PathForRequest()); NavigateToURLContentInitiated(shell(), url3a, false, true); - // There should be two history entries. url2b should have replaced url1. url2b - // should not have replaced url3b. + // There should be two history entries. url2b should have replaced url1. url3b + // should not have replaced url2b. EXPECT_TRUE(controller.GetPendingEntry() == nullptr); EXPECT_EQ(2, controller.GetEntryCount()); EXPECT_EQ(1, controller.GetCurrentEntryIndex()); diff --git a/chromium/content/browser/database_util_unittest.cc b/chromium/content/browser/database_util_unittest.cc index 50d68952b4a..9534cb98488 100644 --- a/chromium/content/browser/database_util_unittest.cc +++ b/chromium/content/browser/database_util_unittest.cc @@ -29,17 +29,6 @@ static void TestVfsFilePath(bool expected_result, EXPECT_EQ(ASCIIToUTF16(expected_sqlite_suffix), sqlite_suffix); } -static GURL ToAndFromOriginIdentifier(const GURL origin_url) { - std::string id = storage::GetIdentifierFromOrigin(origin_url); - return storage::GetOriginFromIdentifier(id); -} - -static void TestValidOriginIdentifier(bool expected_result, - const std::string& id) { - EXPECT_EQ(expected_result, - DatabaseUtil::IsValidOriginIdentifier(id)); -} - namespace content { // Test DatabaseUtil::CrackVfsFilePath on various inputs. @@ -59,21 +48,5 @@ TEST(DatabaseUtilTest, CrackVfsFilePathTest) { TestVfsFilePath(false, "/db_name#suffix"); } -TEST(DatabaseUtilTest, OriginIdentifiers) { - const GURL kFileOrigin(GURL("file:///").GetOrigin()); - const GURL kHttpOrigin(GURL("http://bar/").GetOrigin()); - EXPECT_EQ(kFileOrigin, ToAndFromOriginIdentifier(kFileOrigin)); - EXPECT_EQ(kHttpOrigin, ToAndFromOriginIdentifier(kHttpOrigin)); -} - -TEST(DatabaseUtilTest, IsValidOriginIdentifier) { - TestValidOriginIdentifier(true, "http_bar_0"); - TestValidOriginIdentifier(false, ""); - TestValidOriginIdentifier(false, "bad..id"); - TestValidOriginIdentifier(false, "bad/id"); - TestValidOriginIdentifier(false, "bad\\id"); - TestValidOriginIdentifier(false, "http_bad:0_2"); - TestValidOriginIdentifier(false, std::string("bad\0id", 6)); -} } // namespace content diff --git a/chromium/content/browser/device_monitor_mac.h b/chromium/content/browser/device_monitor_mac.h deleted file mode 100644 index 05eab158db8..00000000000 --- a/chromium/content/browser/device_monitor_mac.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_DEVICE_MONITOR_MAC_H_ -#define CONTENT_BROWSER_DEVICE_MONITOR_MAC_H_ - -#include "base/macros.h" -#include "base/system_monitor/system_monitor.h" -#include "base/threading/thread_checker.h" - -namespace { -class DeviceMonitorMacImpl; -} - -namespace content { - -// Class to track audio/video devices removal or addition via callback to -// base::SystemMonitor ProcessDevicesChanged(). A single object of this class -// is created from the browser main process and lives as long as this one. -class DeviceMonitorMac { - public: - DeviceMonitorMac(); - ~DeviceMonitorMac(); - - // Registers the observers for the audio/video device removal, connection and - // suspension. The AVFoundation library is also loaded and initialised if the - // OS supports it. The |device_task_runner| argument represents the thread on - // which device enumeration will occur. - void StartMonitoring( - const scoped_refptr& device_task_runner); - - // Method called by the internal DeviceMonitorMacImpl object - // |device_monitor_impl_| when a device of type |type| has been added to or - // removed from the system. This code executes in the notification thread - // (QTKit or AVFoundation). - void NotifyDeviceChanged(base::SystemMonitor::DeviceType type); - - private: - scoped_ptr device_monitor_impl_; - - // |thread_checker_| is used to check that constructor and StartMonitoring() - // are called in the correct thread, the UI thread, that also owns the object. - base::ThreadChecker thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMac); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_DEVICE_MONITOR_MAC_H_ diff --git a/chromium/content/browser/device_monitor_mac.mm b/chromium/content/browser/device_monitor_mac.mm deleted file mode 100644 index 248b99dceda..00000000000 --- a/chromium/content/browser/device_monitor_mac.mm +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/device_monitor_mac.h" - -#import - -#include - -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/mac/bind_objc_block.h" -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "base/profiler/scoped_tracker.h" -#include "base/threading/thread_checker.h" -#include "content/public/browser/browser_thread.h" -#import "media/base/mac/avfoundation_glue.h" - -using content::BrowserThread; - -namespace { - -// This class is used to keep track of system devices names and their types. -class DeviceInfo { - public: - enum DeviceType { - kAudio, - kVideo, - kMuxed, - kUnknown, - kInvalid - }; - - DeviceInfo(const std::string& unique_id, DeviceType type) - : unique_id_(unique_id), type_(type) {} - - // Operator== is needed here to use this class in a std::find. A given - // |unique_id_| always has the same |type_| so for comparison purposes the - // latter can be safely ignored. - bool operator==(const DeviceInfo& device) const { - return unique_id_ == device.unique_id_; - } - - const std::string& unique_id() const { return unique_id_; } - DeviceType type() const { return type_; } - - private: - std::string unique_id_; - DeviceType type_; - // Allow generated copy constructor and assignment. -}; - -// Base abstract class used by DeviceMonitorMac to interact with either a QTKit -// or an AVFoundation implementation of events and notifications. -class DeviceMonitorMacImpl { - public: - explicit DeviceMonitorMacImpl(content::DeviceMonitorMac* monitor) - : monitor_(monitor), - cached_devices_(), - device_arrival_(nil), - device_removal_(nil) { - DCHECK(monitor); - // Initialise the devices_cache_ with a not-valid entry. For the case in - // which there is one single device in the system and we get notified when - // it gets removed, this will prevent the system from thinking that no - // devices were added nor removed and not notifying the |monitor_|. - cached_devices_.push_back(DeviceInfo("invalid", DeviceInfo::kInvalid)); - } - virtual ~DeviceMonitorMacImpl() {} - - virtual void OnDeviceChanged() = 0; - - // Method called by the default notification center when a device is removed - // or added to the system. It will compare the |cached_devices_| with the - // current situation, update it, and, if there's an update, signal to - // |monitor_| with the appropriate device type. - void ConsolidateDevicesListAndNotify( - const std::vector& snapshot_devices); - - protected: - content::DeviceMonitorMac* monitor_; - std::vector cached_devices_; - - // Handles to NSNotificationCenter block observers. - id device_arrival_; - id device_removal_; - - private: - DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMacImpl); -}; - -void DeviceMonitorMacImpl::ConsolidateDevicesListAndNotify( - const std::vector& snapshot_devices) { - bool video_device_added = false; - bool audio_device_added = false; - bool video_device_removed = false; - bool audio_device_removed = false; - - // Compare the current system devices snapshot with the ones cached to detect - // additions, present in the former but not in the latter. If we find a device - // in snapshot_devices entry also present in cached_devices, we remove it from - // the latter vector. - std::vector::const_iterator it; - for (it = snapshot_devices.begin(); it != snapshot_devices.end(); ++it) { - std::vector::iterator cached_devices_iterator = - std::find(cached_devices_.begin(), cached_devices_.end(), *it); - if (cached_devices_iterator == cached_devices_.end()) { - video_device_added |= ((it->type() == DeviceInfo::kVideo) || - (it->type() == DeviceInfo::kMuxed)); - audio_device_added |= ((it->type() == DeviceInfo::kAudio) || - (it->type() == DeviceInfo::kMuxed)); - DVLOG(1) << "Device has been added, id: " << it->unique_id(); - } else { - cached_devices_.erase(cached_devices_iterator); - } - } - // All the remaining entries in cached_devices are removed devices. - for (it = cached_devices_.begin(); it != cached_devices_.end(); ++it) { - video_device_removed |= ((it->type() == DeviceInfo::kVideo) || - (it->type() == DeviceInfo::kMuxed) || - (it->type() == DeviceInfo::kInvalid)); - audio_device_removed |= ((it->type() == DeviceInfo::kAudio) || - (it->type() == DeviceInfo::kMuxed) || - (it->type() == DeviceInfo::kInvalid)); - DVLOG(1) << "Device has been removed, id: " << it->unique_id(); - } - // Update the cached devices with the current system snapshot. - cached_devices_ = snapshot_devices; - - if (video_device_added || video_device_removed) - monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); - if (audio_device_added || audio_device_removed) - monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE); -} - -class QTKitMonitorImpl : public DeviceMonitorMacImpl { - public: - explicit QTKitMonitorImpl(content::DeviceMonitorMac* monitor); - ~QTKitMonitorImpl() override; - - void OnDeviceChanged() override; - - private: - void CountDevices(); - void OnAttributeChanged(NSNotification* notification); - - id device_change_; -}; - -QTKitMonitorImpl::QTKitMonitorImpl(content::DeviceMonitorMac* monitor) - : DeviceMonitorMacImpl(monitor) { - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - device_arrival_ = - [nc addObserverForName:QTCaptureDeviceWasConnectedNotification - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - OnDeviceChanged();}]; - device_removal_ = - [nc addObserverForName:QTCaptureDeviceWasDisconnectedNotification - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - OnDeviceChanged();}]; - device_change_ = - [nc addObserverForName:QTCaptureDeviceAttributeDidChangeNotification - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - OnAttributeChanged(notification);}]; -} - -QTKitMonitorImpl::~QTKitMonitorImpl() { - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:device_arrival_]; - [nc removeObserver:device_removal_]; - [nc removeObserver:device_change_]; -} - -void QTKitMonitorImpl::OnAttributeChanged( - NSNotification* notification) { - if ([[[notification userInfo] - objectForKey:QTCaptureDeviceChangedAttributeKey] - isEqualToString:QTCaptureDeviceSuspendedAttribute]) { - OnDeviceChanged(); - } -} - -void QTKitMonitorImpl::OnDeviceChanged() { - std::vector snapshot_devices; - - NSArray* devices = [QTCaptureDevice inputDevices]; - for (QTCaptureDevice* device in devices) { - DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; - // Act as if suspended video capture devices are not attached. For - // example, a laptop's internal webcam is suspended when the lid is closed. - if ([device hasMediaType:QTMediaTypeVideo] && - ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute] - boolValue]) { - device_type = DeviceInfo::kVideo; - } else if ([device hasMediaType:QTMediaTypeMuxed] && - ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute] - boolValue]) { - device_type = DeviceInfo::kMuxed; - } else if ([device hasMediaType:QTMediaTypeSound] && - ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute] - boolValue]) { - device_type = DeviceInfo::kAudio; - } - snapshot_devices.push_back( - DeviceInfo([[device uniqueID] UTF8String], device_type)); - } - ConsolidateDevicesListAndNotify(snapshot_devices); -} - -// Forward declaration for use by CrAVFoundationDeviceObserver. -class SuspendObserverDelegate; - -} // namespace - -// This class is a Key-Value Observer (KVO) shim. It is needed because C++ -// classes cannot observe Key-Values directly. Created, manipulated, and -// destroyed on the UI Thread by SuspendObserverDelegate. -@interface CrAVFoundationDeviceObserver : NSObject { - @private - // Callback for device changed, has to run on Device Thread. - base::Closure onDeviceChangedCallback_; - - // Member to keep track of the devices we are already monitoring. - std::set > monitoredDevices_; -} - -- (id)initWithOnChangedCallback:(const base::Closure&)callback; -- (void)startObserving:(base::scoped_nsobject)device; -- (void)stopObserving:(CrAVCaptureDevice*)device; -- (void)clearOnDeviceChangedCallback; - -@end - -namespace { - -// This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. -// It is created and destroyed in UI thread by AVFoundationMonitorImpl, and it -// operates on this thread except for the expensive device enumerations which -// are run on Device Thread. -class SuspendObserverDelegate : - public base::RefCountedThreadSafe { - public: - explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor); - - // Create |suspend_observer_| for all devices and register OnDeviceChanged() - // as its change callback. Schedule bottom half in DoStartObserver(). - void StartObserver( - const scoped_refptr& device_thread); - // Enumerate devices in |device_thread| and run the bottom half in - // DoOnDeviceChange(). |suspend_observer_| calls back here on suspend event, - // and our parent AVFoundationMonitorImpl calls on connect/disconnect device. - void OnDeviceChanged( - const scoped_refptr& device_thread); - // Remove the device monitor's weak reference. Remove ourselves as suspend - // notification observer from |suspend_observer_|. - void ResetDeviceMonitor(); - - private: - friend class base::RefCountedThreadSafe; - - virtual ~SuspendObserverDelegate(); - - // Bottom half of StartObserver(), starts |suspend_observer_| for all devices. - // Assumes that |devices| has been retained prior to being called, and - // releases it internally. - void DoStartObserver(NSArray* devices); - // Bottom half of OnDeviceChanged(), starts |suspend_observer_| for current - // devices and composes a snapshot of them to send it to - // |avfoundation_monitor_impl_|. Assumes that |devices| has been retained - // prior to being called, and releases it internally. - void DoOnDeviceChanged(NSArray* devices); - - base::scoped_nsobject suspend_observer_; - DeviceMonitorMacImpl* avfoundation_monitor_impl_; -}; - -SuspendObserverDelegate::SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) - : avfoundation_monitor_impl_(monitor) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); -} - -void SuspendObserverDelegate::StartObserver( - const scoped_refptr& device_thread) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - base::Closure on_device_changed_callback = - base::Bind(&SuspendObserverDelegate::OnDeviceChanged, - this, device_thread); - suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] - initWithOnChangedCallback:on_device_changed_callback]); - - // Enumerate the devices in Device thread and post the observers start to be - // done on UI thread. The devices array is retained in |device_thread| and - // released in DoStartObserver(). - base::PostTaskAndReplyWithResult( - device_thread.get(), - FROM_HERE, - base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }), - base::Bind(&SuspendObserverDelegate::DoStartObserver, this)); -} - -void SuspendObserverDelegate::OnDeviceChanged( - const scoped_refptr& device_thread) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // Enumerate the devices in Device thread and post the consolidation of the - // new devices and the old ones to be done on UI thread. The devices array - // is retained in |device_thread| and released in DoOnDeviceChanged(). - PostTaskAndReplyWithResult( - device_thread.get(), - FROM_HERE, - base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }), - base::Bind(&SuspendObserverDelegate::DoOnDeviceChanged, this)); -} - -void SuspendObserverDelegate::ResetDeviceMonitor() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - avfoundation_monitor_impl_ = NULL; - [suspend_observer_ clearOnDeviceChangedCallback]; -} - -SuspendObserverDelegate::~SuspendObserverDelegate() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); -} - -void SuspendObserverDelegate::DoStartObserver(NSArray* devices) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::scoped_nsobject auto_release(devices); - for (CrAVCaptureDevice* device in devices) { - base::scoped_nsobject device_ptr([device retain]); - [suspend_observer_ startObserving:device_ptr]; - } -} - -void SuspendObserverDelegate::DoOnDeviceChanged(NSArray* devices) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::scoped_nsobject auto_release(devices); - std::vector snapshot_devices; - for (CrAVCaptureDevice* device in devices) { - base::scoped_nsobject device_ptr([device retain]); - [suspend_observer_ startObserving:device_ptr]; - - BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && - [device isSuspended]; - DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; - if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { - if (suspended) - continue; - device_type = DeviceInfo::kVideo; - } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { - device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; - } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { - device_type = DeviceInfo::kAudio; - } - snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], - device_type)); - } - // Make sure no references are held to |devices| when - // ConsolidateDevicesListAndNotify is called since the VideoCaptureManager - // and AudioCaptureManagers also enumerates the available devices but on - // another thread. - auto_release.reset(); - // |avfoundation_monitor_impl_| might have been NULLed asynchronously before - // arriving at this line. - if (avfoundation_monitor_impl_) { - avfoundation_monitor_impl_->ConsolidateDevicesListAndNotify( - snapshot_devices); - } -} - -// AVFoundation implementation of the Mac Device Monitor, registers as a global -// device connect/disconnect observer and plugs suspend/wake up device observers -// per device. This class is created and lives in UI thread. Owns a -// SuspendObserverDelegate that notifies when a device is suspended/resumed. -class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { - public: - AVFoundationMonitorImpl( - content::DeviceMonitorMac* monitor, - const scoped_refptr& device_task_runner); - ~AVFoundationMonitorImpl() override; - - void OnDeviceChanged() override; - - private: - // {Video,AudioInput}DeviceManager's "Device" thread task runner used for - // posting tasks to |suspend_observer_delegate_|; valid after - // MediaStreamManager calls StartMonitoring(). - const scoped_refptr device_task_runner_; - - scoped_refptr suspend_observer_delegate_; - - DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); -}; - -AVFoundationMonitorImpl::AVFoundationMonitorImpl( - content::DeviceMonitorMac* monitor, - const scoped_refptr& device_task_runner) - : DeviceMonitorMacImpl(monitor), - device_task_runner_(device_task_runner), - suspend_observer_delegate_(new SuspendObserverDelegate(this)) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - device_arrival_ = - [nc addObserverForName:AVFoundationGlue:: - AVCaptureDeviceWasConnectedNotification() - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - OnDeviceChanged();}]; - device_removal_ = - [nc addObserverForName:AVFoundationGlue:: - AVCaptureDeviceWasDisconnectedNotification() - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - OnDeviceChanged();}]; - suspend_observer_delegate_->StartObserver(device_task_runner_); -} - -AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - suspend_observer_delegate_->ResetDeviceMonitor(); - NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:device_arrival_]; - [nc removeObserver:device_removal_]; -} - -void AVFoundationMonitorImpl::OnDeviceChanged() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - suspend_observer_delegate_->OnDeviceChanged(device_task_runner_); -} - -} // namespace - -@implementation CrAVFoundationDeviceObserver - -- (id)initWithOnChangedCallback:(const base::Closure&)callback { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if ((self = [super init])) { - DCHECK(!callback.is_null()); - onDeviceChangedCallback_ = callback; - } - return self; -} - -- (void)dealloc { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - std::set >::iterator it = - monitoredDevices_.begin(); - while (it != monitoredDevices_.end()) - [self removeObservers:*(it++)]; - [super dealloc]; -} - -- (void)startObserving:(base::scoped_nsobject)device { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(device != nil); - // Skip this device if there are already observers connected to it. - if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != - monitoredDevices_.end()) { - return; - } - [device addObserver:self - forKeyPath:@"suspended" - options:0 - context:device.get()]; - [device addObserver:self - forKeyPath:@"connected" - options:0 - context:device.get()]; - monitoredDevices_.insert(device); -} - -- (void)stopObserving:(CrAVCaptureDevice*)device { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(device != nil); - - std::set >::iterator found = - std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); - DCHECK(found != monitoredDevices_.end()); - [self removeObservers:*found]; - monitoredDevices_.erase(found); -} - -- (void)clearOnDeviceChangedCallback { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - onDeviceChangedCallback_.Reset(); -} - -- (void)removeObservers:(CrAVCaptureDevice*)device { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // Check sanity of |device| via its -observationInfo. http://crbug.com/371271. - if ([device observationInfo]) { - [device removeObserver:self - forKeyPath:@"suspended"]; - [device removeObserver:self - forKeyPath:@"connected"]; - } -} - -- (void)observeValueForKeyPath:(NSString*)keyPath - ofObject:(id)object - change:(NSDictionary*)change - context:(void*)context { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if ([keyPath isEqual:@"suspended"]) - onDeviceChangedCallback_.Run(); - if ([keyPath isEqual:@"connected"]) - [self stopObserving:static_cast(context)]; -} - -@end // @implementation CrAVFoundationDeviceObserver - -namespace content { - -DeviceMonitorMac::DeviceMonitorMac() { - // Both QTKit and AVFoundation do not need to be fired up until the user - // exercises a GetUserMedia. Bringing up either library and enumerating the - // devices in the system is an operation taking in the range of hundred of ms, - // so it is triggered explicitly from MediaStreamManager::StartMonitoring(). -} - -DeviceMonitorMac::~DeviceMonitorMac() {} - -void DeviceMonitorMac::StartMonitoring( - const scoped_refptr& device_task_runner) { - DCHECK(thread_checker_.CalledOnValidThread()); - - // We're on the UI thread so let's try to initialize AVFoundation and then - // see if it's supported. IsAVFoundationSupported can't implicitly initialize - // the library since it can be called on different threads. - AVFoundationGlue::InitializeAVFoundation(); - - if (AVFoundationGlue::IsAVFoundationSupported()) { - // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458404 - // is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION( - "458404 DeviceMonitorMac::StartMonitoring::AVFoundation")); - DVLOG(1) << "Monitoring via AVFoundation"; - device_monitor_impl_.reset(new AVFoundationMonitorImpl(this, - device_task_runner)); - } else { - // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458404 - // is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION( - "458404 DeviceMonitorMac::StartMonitoring::QTKit")); - DVLOG(1) << "Monitoring via QTKit"; - device_monitor_impl_.reset(new QTKitMonitorImpl(this)); - } -} - -void DeviceMonitorMac::NotifyDeviceChanged( - base::SystemMonitor::DeviceType type) { - DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(xians): Remove the global variable for SystemMonitor. - base::SystemMonitor::Get()->ProcessDevicesChanged(type); -} - -} // namespace content diff --git a/chromium/content/browser/device_monitor_udev.cc b/chromium/content/browser/device_monitor_udev.cc deleted file mode 100644 index 822e995f47c..00000000000 --- a/chromium/content/browser/device_monitor_udev.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// libudev is used for monitoring device changes. - -#include "content/browser/device_monitor_udev.h" - -#include - -#include - -#include "base/macros.h" -#include "base/system_monitor/system_monitor.h" -#include "content/browser/udev_linux.h" -#include "content/public/browser/browser_thread.h" -#include "device/udev_linux/udev.h" - -namespace { - -struct SubsystemMap { - base::SystemMonitor::DeviceType device_type; - const char* subsystem; - const char* devtype; -}; - -const char kAudioSubsystem[] = "sound"; -const char kVideoSubsystem[] = "video4linux"; - -// Add more subsystems here for monitoring. -const SubsystemMap kSubsystemMap[] = { - { base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE, kAudioSubsystem, NULL }, - { base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kVideoSubsystem, NULL }, -}; - -} // namespace - -namespace content { - -DeviceMonitorLinux::DeviceMonitorLinux() { - DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO)); - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&DeviceMonitorLinux::Initialize, base::Unretained(this))); -} - -DeviceMonitorLinux::~DeviceMonitorLinux() { -} - -void DeviceMonitorLinux::Initialize() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // We want to be notified of IO message loop destruction to delete |udev_|. - base::MessageLoop::current()->AddDestructionObserver(this); - - std::vector filters; - for (size_t i = 0; i < arraysize(kSubsystemMap); ++i) { - filters.push_back(UdevLinux::UdevMonitorFilter( - kSubsystemMap[i].subsystem, kSubsystemMap[i].devtype)); - } - udev_.reset(new UdevLinux(filters, - base::Bind(&DeviceMonitorLinux::OnDevicesChanged, - base::Unretained(this)))); -} - -void DeviceMonitorLinux::WillDestroyCurrentMessageLoop() { - // Called on IO thread. - udev_.reset(); -} - -void DeviceMonitorLinux::OnDevicesChanged(udev_device* device) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(device); - - base::SystemMonitor::DeviceType device_type = - base::SystemMonitor::DEVTYPE_UNKNOWN; - std::string subsystem(device::udev_device_get_subsystem(device)); - for (size_t i = 0; i < arraysize(kSubsystemMap); ++i) { - if (subsystem == kSubsystemMap[i].subsystem) { - device_type = kSubsystemMap[i].device_type; - break; - } - } - DCHECK_NE(device_type, base::SystemMonitor::DEVTYPE_UNKNOWN); - - base::SystemMonitor::Get()->ProcessDevicesChanged(device_type); -} - -} // namespace content diff --git a/chromium/content/browser/device_monitor_udev.h b/chromium/content/browser/device_monitor_udev.h deleted file mode 100644 index 965055d93b2..00000000000 --- a/chromium/content/browser/device_monitor_udev.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This class is used to detect device change and notify base::SystemMonitor -// on Linux. - -#ifndef CONTENT_BROWSER_DEVICE_MONITOR_UDEV_H_ -#define CONTENT_BROWSER_DEVICE_MONITOR_UDEV_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" - -extern "C" { -struct udev_device; -} - -namespace content { - -class UdevLinux; - -class DeviceMonitorLinux : public base::MessageLoop::DestructionObserver { - public: - DeviceMonitorLinux(); - ~DeviceMonitorLinux() override; - - private: - // This object is deleted on the UI thread after the IO thread has been - // destroyed. Need to know when IO thread is being destroyed so that - // we can delete udev_. - void WillDestroyCurrentMessageLoop() override; - - void Initialize(); - void OnDevicesChanged(udev_device* device); - - scoped_ptr udev_; - - DISALLOW_COPY_AND_ASSIGN(DeviceMonitorLinux); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_DEVICE_MONITOR_UDEV_H_ diff --git a/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc index a9ab2ff1dc2..c8b37986c7d 100644 --- a/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc +++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_mac.cc @@ -194,7 +194,6 @@ bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) { // On Mac we cannot provide absolute orientation. orientation_buffer_->seqlock.WriteBegin(); orientation_buffer_->data.absolute = false; - orientation_buffer_->data.hasAbsolute = true; orientation_buffer_->seqlock.WriteEnd(); } else { // No motion sensor available, fire an all-null event. diff --git a/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc index 709c25b595c..40471ca0a3b 100644 --- a/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc +++ b/chromium/content/browser/device_sensors/data_fetcher_shared_memory_win.cc @@ -129,8 +129,7 @@ class DataFetcherSharedMemory::SensorEventSinkOrientation buffer_->data.hasBeta = has_beta; buffer_->data.gamma = gamma; buffer_->data.hasGamma = has_gamma; - buffer_->data.absolute = true; - buffer_->data.hasAbsolute = has_alpha || has_beta || has_gamma; + buffer_->data.absolute = has_alpha || has_beta || has_gamma; buffer_->data.allAvailableSensorsAreActive = true; buffer_->seqlock.WriteEnd(); } diff --git a/chromium/content/browser/device_sensors/device_light_message_filter.cc b/chromium/content/browser/device_sensors/device_light_message_filter.cc index 797b3eade2c..dda05a86aa8 100644 --- a/chromium/content/browser/device_sensors/device_light_message_filter.cc +++ b/chromium/content/browser/device_sensors/device_light_message_filter.cc @@ -4,57 +4,28 @@ #include "content/browser/device_sensors/device_light_message_filter.h" -#include "content/browser/device_sensors/device_inertial_sensor_service.h" #include "content/common/device_sensors/device_light_messages.h" namespace content { DeviceLightMessageFilter::DeviceLightMessageFilter() - : BrowserMessageFilter(DeviceLightMsgStart), is_started_(false) { -} + : DeviceSensorMessageFilter(CONSUMER_TYPE_LIGHT, DeviceLightMsgStart) {} -DeviceLightMessageFilter::~DeviceLightMessageFilter() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (is_started_) { - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_LIGHT); - } -} +DeviceLightMessageFilter::~DeviceLightMessageFilter() {} bool DeviceLightMessageFilter::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(DeviceLightMessageFilter, message) - IPC_MESSAGE_HANDLER(DeviceLightHostMsg_StartPolling, - OnDeviceLightStartPolling) - IPC_MESSAGE_HANDLER(DeviceLightHostMsg_StopPolling, OnDeviceLightStopPolling) + IPC_MESSAGE_HANDLER(DeviceLightHostMsg_StartPolling, OnStartPolling) + IPC_MESSAGE_HANDLER(DeviceLightHostMsg_StopPolling, OnStopPolling) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } -void DeviceLightMessageFilter::OnDeviceLightStartPolling() { - DCHECK(!is_started_); - if (is_started_) - return; - is_started_ = true; - DeviceInertialSensorService::GetInstance()->AddConsumer(CONSUMER_TYPE_LIGHT); - DidStartDeviceLightPolling(); -} - -void DeviceLightMessageFilter::OnDeviceLightStopPolling() { - DCHECK(is_started_); - if (!is_started_) - return; - is_started_ = false; - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_LIGHT); -} - -void DeviceLightMessageFilter::DidStartDeviceLightPolling() { - Send(new DeviceLightMsg_DidStartPolling( - DeviceInertialSensorService::GetInstance() - ->GetSharedMemoryHandleForProcess(CONSUMER_TYPE_LIGHT, - PeerHandle()))); +void DeviceLightMessageFilter::DidStartPolling( + base::SharedMemoryHandle handle) { + Send(new DeviceLightMsg_DidStartPolling(handle)); } } // namespace content diff --git a/chromium/content/browser/device_sensors/device_light_message_filter.h b/chromium/content/browser/device_sensors/device_light_message_filter.h index 58246935a7e..bf302b7dd5a 100644 --- a/chromium/content/browser/device_sensors/device_light_message_filter.h +++ b/chromium/content/browser/device_sensors/device_light_message_filter.h @@ -5,12 +5,11 @@ #ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_LIGHT_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_LIGHT_MESSAGE_FILTER_H_ -#include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" +#include "content/browser/device_sensors/device_sensor_message_filter.h" namespace content { -class DeviceLightMessageFilter : public BrowserMessageFilter { +class DeviceLightMessageFilter : public DeviceSensorMessageFilter { public: DeviceLightMessageFilter(); @@ -20,11 +19,8 @@ class DeviceLightMessageFilter : public BrowserMessageFilter { private: ~DeviceLightMessageFilter() override; - void OnDeviceLightStartPolling(); - void OnDeviceLightStopPolling(); - void DidStartDeviceLightPolling(); - - bool is_started_; + // DeviceSensorMessageFilter implementation. + void DidStartPolling(base::SharedMemoryHandle handle) override; DISALLOW_COPY_AND_ASSIGN(DeviceLightMessageFilter); }; diff --git a/chromium/content/browser/device_sensors/device_motion_message_filter.cc b/chromium/content/browser/device_sensors/device_motion_message_filter.cc index f874ea493a4..58a2b514b5b 100644 --- a/chromium/content/browser/device_sensors/device_motion_message_filter.cc +++ b/chromium/content/browser/device_sensors/device_motion_message_filter.cc @@ -4,59 +4,28 @@ #include "content/browser/device_sensors/device_motion_message_filter.h" -#include "content/browser/device_sensors/device_inertial_sensor_service.h" #include "content/common/device_sensors/device_motion_messages.h" namespace content { DeviceMotionMessageFilter::DeviceMotionMessageFilter() - : BrowserMessageFilter(DeviceMotionMsgStart), - is_started_(false) { -} + : DeviceSensorMessageFilter(CONSUMER_TYPE_MOTION, DeviceMotionMsgStart) {} -DeviceMotionMessageFilter::~DeviceMotionMessageFilter() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (is_started_) - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_MOTION); -} +DeviceMotionMessageFilter::~DeviceMotionMessageFilter() {} bool DeviceMotionMessageFilter::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(DeviceMotionMessageFilter, message) - IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StartPolling, - OnDeviceMotionStartPolling) - IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StopPolling, - OnDeviceMotionStopPolling) + IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StartPolling, OnStartPolling) + IPC_MESSAGE_HANDLER(DeviceMotionHostMsg_StopPolling, OnStopPolling) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } -void DeviceMotionMessageFilter::OnDeviceMotionStartPolling() { - DCHECK(!is_started_); - if (is_started_) - return; - is_started_ = true; - DeviceInertialSensorService::GetInstance()->AddConsumer( - CONSUMER_TYPE_MOTION); - DidStartDeviceMotionPolling(); -} - -void DeviceMotionMessageFilter::OnDeviceMotionStopPolling() { - DCHECK(is_started_); - if (!is_started_) - return; - is_started_ = false; - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_MOTION); -} - -void DeviceMotionMessageFilter::DidStartDeviceMotionPolling() { - Send(new DeviceMotionMsg_DidStartPolling( - DeviceInertialSensorService::GetInstance()-> - GetSharedMemoryHandleForProcess( - CONSUMER_TYPE_MOTION, PeerHandle()))); +void DeviceMotionMessageFilter::DidStartPolling( + base::SharedMemoryHandle handle) { + Send(new DeviceMotionMsg_DidStartPolling(handle)); } } // namespace content diff --git a/chromium/content/browser/device_sensors/device_motion_message_filter.h b/chromium/content/browser/device_sensors/device_motion_message_filter.h index 36eb42aa127..3b0587d15da 100644 --- a/chromium/content/browser/device_sensors/device_motion_message_filter.h +++ b/chromium/content/browser/device_sensors/device_motion_message_filter.h @@ -5,12 +5,11 @@ #ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_MOTION_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_MOTION_MESSAGE_FILTER_H_ -#include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" +#include "content/browser/device_sensors/device_sensor_message_filter.h" namespace content { -class DeviceMotionMessageFilter : public BrowserMessageFilter { +class DeviceMotionMessageFilter : public DeviceSensorMessageFilter { public: DeviceMotionMessageFilter(); @@ -20,11 +19,8 @@ class DeviceMotionMessageFilter : public BrowserMessageFilter { private: ~DeviceMotionMessageFilter() override; - void OnDeviceMotionStartPolling(); - void OnDeviceMotionStopPolling(); - void DidStartDeviceMotionPolling(); - - bool is_started_; + // DeviceSensorMessageFilter implementation. + void DidStartPolling(base::SharedMemoryHandle handle) override; DISALLOW_COPY_AND_ASSIGN(DeviceMotionMessageFilter); }; diff --git a/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.cc b/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.cc index 572952cea11..3aa6042e429 100644 --- a/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.cc +++ b/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.cc @@ -4,24 +4,16 @@ #include "content/browser/device_sensors/device_orientation_absolute_message_filter.h" -#include "content/browser/device_sensors/device_inertial_sensor_service.h" #include "content/common/device_sensors/device_orientation_messages.h" namespace content { DeviceOrientationAbsoluteMessageFilter::DeviceOrientationAbsoluteMessageFilter() - : BrowserMessageFilter(DeviceOrientationMsgStart), - is_started_(false) { -} + : DeviceSensorMessageFilter(CONSUMER_TYPE_ORIENTATION_ABSOLUTE, + DeviceOrientationMsgStart) {} DeviceOrientationAbsoluteMessageFilter:: - ~DeviceOrientationAbsoluteMessageFilter() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (is_started_) { - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_ORIENTATION_ABSOLUTE); - } -} + ~DeviceOrientationAbsoluteMessageFilter() {} bool DeviceOrientationAbsoluteMessageFilter::OnMessageReceived( const IPC::Message& message) { @@ -36,31 +28,9 @@ bool DeviceOrientationAbsoluteMessageFilter::OnMessageReceived( return handled; } -void DeviceOrientationAbsoluteMessageFilter::OnStartPolling() { - DCHECK(!is_started_); - if (is_started_) - return; - is_started_ = true; - DeviceInertialSensorService::GetInstance()->AddConsumer( - CONSUMER_TYPE_ORIENTATION_ABSOLUTE); - DidStartPolling(); -} - -void DeviceOrientationAbsoluteMessageFilter::OnStopPolling() { - DCHECK(is_started_); - if (!is_started_) - return; - is_started_ = false; - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_ORIENTATION_ABSOLUTE); -} - -void DeviceOrientationAbsoluteMessageFilter::DidStartPolling() { - Send(new DeviceOrientationAbsoluteMsg_DidStartPolling( - DeviceInertialSensorService::GetInstance()-> - GetSharedMemoryHandleForProcess( - CONSUMER_TYPE_ORIENTATION_ABSOLUTE, - PeerHandle()))); +void DeviceOrientationAbsoluteMessageFilter::DidStartPolling( + base::SharedMemoryHandle handle) { + Send(new DeviceOrientationAbsoluteMsg_DidStartPolling(handle)); } } // namespace content diff --git a/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.h b/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.h index 5ed5ac5467e..5e64bf8fe73 100644 --- a/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.h +++ b/chromium/content/browser/device_sensors/device_orientation_absolute_message_filter.h @@ -5,12 +5,12 @@ #ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_ABSOLUTE_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_ABSOLUTE_MESSAGE_FILTER_H_ -#include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" +#include "content/browser/device_sensors/device_sensor_message_filter.h" namespace content { -class DeviceOrientationAbsoluteMessageFilter : public BrowserMessageFilter { +class DeviceOrientationAbsoluteMessageFilter + : public DeviceSensorMessageFilter { public: DeviceOrientationAbsoluteMessageFilter(); @@ -20,11 +20,8 @@ class DeviceOrientationAbsoluteMessageFilter : public BrowserMessageFilter { private: ~DeviceOrientationAbsoluteMessageFilter() override; - void OnStartPolling(); - void OnStopPolling(); - void DidStartPolling(); - - bool is_started_; + // DeviceSensorMessageFilter implementation. + void DidStartPolling(base::SharedMemoryHandle handle) override; DISALLOW_COPY_AND_ASSIGN(DeviceOrientationAbsoluteMessageFilter); }; diff --git a/chromium/content/browser/device_sensors/device_orientation_message_filter.cc b/chromium/content/browser/device_sensors/device_orientation_message_filter.cc index 63a9d6230f4..9b0aeb8b13a 100644 --- a/chromium/content/browser/device_sensors/device_orientation_message_filter.cc +++ b/chromium/content/browser/device_sensors/device_orientation_message_filter.cc @@ -4,61 +4,30 @@ #include "content/browser/device_sensors/device_orientation_message_filter.h" -#include "content/browser/device_sensors/device_inertial_sensor_service.h" #include "content/common/device_sensors/device_orientation_messages.h" namespace content { DeviceOrientationMessageFilter::DeviceOrientationMessageFilter() - : BrowserMessageFilter(DeviceOrientationMsgStart), - is_started_(false) { -} + : DeviceSensorMessageFilter(CONSUMER_TYPE_ORIENTATION, + DeviceOrientationMsgStart) {} -DeviceOrientationMessageFilter::~DeviceOrientationMessageFilter() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (is_started_) - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_ORIENTATION); -} +DeviceOrientationMessageFilter::~DeviceOrientationMessageFilter() {} bool DeviceOrientationMessageFilter::OnMessageReceived( const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(DeviceOrientationMessageFilter, message) - IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StartPolling, - OnDeviceOrientationStartPolling) - IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StopPolling, - OnDeviceOrientationStopPolling) + IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StartPolling, OnStartPolling) + IPC_MESSAGE_HANDLER(DeviceOrientationHostMsg_StopPolling, OnStopPolling) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } -void DeviceOrientationMessageFilter::OnDeviceOrientationStartPolling() { - DCHECK(!is_started_); - if (is_started_) - return; - is_started_ = true; - DeviceInertialSensorService::GetInstance()->AddConsumer( - CONSUMER_TYPE_ORIENTATION); - DidStartDeviceOrientationPolling(); -} - -void DeviceOrientationMessageFilter::OnDeviceOrientationStopPolling() { - DCHECK(is_started_); - if (!is_started_) - return; - is_started_ = false; - DeviceInertialSensorService::GetInstance()->RemoveConsumer( - CONSUMER_TYPE_ORIENTATION); -} - -void DeviceOrientationMessageFilter::DidStartDeviceOrientationPolling() { - Send(new DeviceOrientationMsg_DidStartPolling( - DeviceInertialSensorService::GetInstance()-> - GetSharedMemoryHandleForProcess( - CONSUMER_TYPE_ORIENTATION, - PeerHandle()))); +void DeviceOrientationMessageFilter::DidStartPolling( + base::SharedMemoryHandle handle) { + Send(new DeviceOrientationMsg_DidStartPolling(handle)); } } // namespace content diff --git a/chromium/content/browser/device_sensors/device_orientation_message_filter.h b/chromium/content/browser/device_sensors/device_orientation_message_filter.h index fb375d99439..21e445347a7 100644 --- a/chromium/content/browser/device_sensors/device_orientation_message_filter.h +++ b/chromium/content/browser/device_sensors/device_orientation_message_filter.h @@ -5,12 +5,11 @@ #ifndef CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_MESSAGE_FILTER_H_ #define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_ORIENTATION_MESSAGE_FILTER_H_ -#include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" +#include "content/browser/device_sensors/device_sensor_message_filter.h" namespace content { -class DeviceOrientationMessageFilter : public BrowserMessageFilter { +class DeviceOrientationMessageFilter : public DeviceSensorMessageFilter { public: DeviceOrientationMessageFilter(); @@ -20,11 +19,8 @@ class DeviceOrientationMessageFilter : public BrowserMessageFilter { private: ~DeviceOrientationMessageFilter() override; - void OnDeviceOrientationStartPolling(); - void OnDeviceOrientationStopPolling(); - void DidStartDeviceOrientationPolling(); - - bool is_started_; + // DeviceSensorMessageFilter implementation. + void DidStartPolling(base::SharedMemoryHandle handle) override; DISALLOW_COPY_AND_ASSIGN(DeviceOrientationMessageFilter); }; diff --git a/chromium/content/browser/device_sensors/device_sensor_message_filter.cc b/chromium/content/browser/device_sensors/device_sensor_message_filter.cc new file mode 100644 index 00000000000..cb9e9e592a4 --- /dev/null +++ b/chromium/content/browser/device_sensors/device_sensor_message_filter.cc @@ -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. + +#include "content/browser/device_sensors/device_sensor_message_filter.h" + +#include "content/browser/device_sensors/device_inertial_sensor_service.h" + +namespace content { + +DeviceSensorMessageFilter::DeviceSensorMessageFilter( + ConsumerType consumer_type, uint32_t message_class_to_filter) + : BrowserMessageFilter(message_class_to_filter), + consumer_type_(consumer_type), + is_started_(false) { +} + +DeviceSensorMessageFilter::~DeviceSensorMessageFilter() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (is_started_) + DeviceInertialSensorService::GetInstance()->RemoveConsumer(consumer_type_); +} + +void DeviceSensorMessageFilter::OnStartPolling() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!is_started_); + if (is_started_) + return; + is_started_ = true; + DeviceInertialSensorService::GetInstance()->AddConsumer(consumer_type_); + DidStartPolling(DeviceInertialSensorService::GetInstance()-> + GetSharedMemoryHandleForProcess(consumer_type_, PeerHandle())); +} + +void DeviceSensorMessageFilter::OnStopPolling() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(is_started_); + if (!is_started_) + return; + is_started_ = false; + DeviceInertialSensorService::GetInstance()->RemoveConsumer(consumer_type_); +} + +} // namespace content diff --git a/chromium/content/browser/device_sensors/device_sensor_message_filter.h b/chromium/content/browser/device_sensors/device_sensor_message_filter.h new file mode 100644 index 00000000000..13d0b0ac3ea --- /dev/null +++ b/chromium/content/browser/device_sensors/device_sensor_message_filter.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 CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_SENSOR_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_SENSOR_MESSAGE_FILTER_H_ + +#include "base/macros.h" +#include "base/memory/shared_memory.h" +#include "content/browser/device_sensors/device_sensors_consts.h" +#include "content/public/browser/browser_message_filter.h" + +namespace content { + +// A base class for device sensor related message filters. +class DeviceSensorMessageFilter : public BrowserMessageFilter { + public: + DeviceSensorMessageFilter(ConsumerType consumer_type, + uint32_t message_class_to_filter); + + protected: + // All methods below to be called on the IO thread. + ~DeviceSensorMessageFilter() override; + + virtual void OnStartPolling(); + virtual void OnStopPolling(); + // To be overriden by the subclass in order to send the appropriate message + // to the renderer with a handle to shared memory. + virtual void DidStartPolling(base::SharedMemoryHandle handle) = 0; + + private: + ConsumerType consumer_type_; + bool is_started_; + + DISALLOW_COPY_AND_ASSIGN(DeviceSensorMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVICE_SENSORS_DEVICE_SENSOR_MESSAGE_FILTER_H_ diff --git a/chromium/content/browser/device_sensors/sensor_manager_android.cc b/chromium/content/browser/device_sensors/sensor_manager_android.cc index e821fb445a4..5fffdd90720 100644 --- a/chromium/content/browser/device_sensors/sensor_manager_android.cc +++ b/chromium/content/browser/device_sensors/sensor_manager_android.cc @@ -41,7 +41,6 @@ void SetOrientationBufferStatus( bool ready, bool absolute) { buffer->seqlock.WriteBegin(); buffer->data.absolute = absolute; - buffer->data.hasAbsolute = ready; buffer->data.allAvailableSensorsAreActive = ready; buffer->seqlock.WriteEnd(); } diff --git a/chromium/content/browser/device_sensors/sensor_manager_chromeos.cc b/chromium/content/browser/device_sensors/sensor_manager_chromeos.cc index 97dc8fbcbc6..3544749d6e4 100644 --- a/chromium/content/browser/device_sensors/sensor_manager_chromeos.cc +++ b/chromium/content/browser/device_sensors/sensor_manager_chromeos.cc @@ -69,7 +69,6 @@ void SensorManagerChromeOS::StartFetchingDeviceOrientationData( // No compass information, so we cannot provide absolute orientation. orientation_buffer_->seqlock.WriteBegin(); orientation_buffer_->data.absolute = false; - orientation_buffer_->data.hasAbsolute = true; orientation_buffer_->seqlock.WriteEnd(); if (!motion_buffer_) diff --git a/chromium/content/browser/device_sensors/sensor_manager_chromeos_unittest.cc b/chromium/content/browser/device_sensors/sensor_manager_chromeos_unittest.cc index 6d9f77644f7..7693e4d81b4 100644 --- a/chromium/content/browser/device_sensors/sensor_manager_chromeos_unittest.cc +++ b/chromium/content/browser/device_sensors/sensor_manager_chromeos_unittest.cc @@ -116,7 +116,6 @@ TEST_F(SensorManagerChromeOSTest, MotionBuffer) { // buffer. TEST_F(SensorManagerChromeOSTest, OrientationBuffer) { DeviceOrientationHardwareBuffer* buffer = orientation_buffer(); - EXPECT_TRUE(buffer->data.hasAbsolute); EXPECT_FALSE(buffer->data.hasAlpha); EXPECT_FALSE(buffer->data.hasBeta); EXPECT_FALSE(buffer->data.hasGamma); diff --git a/chromium/content/browser/devtools/browser_devtools_agent_host.cc b/chromium/content/browser/devtools/browser_devtools_agent_host.cc index d7c342307db..dbc28a60092 100644 --- a/chromium/content/browser/devtools/browser_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/browser_devtools_agent_host.cc @@ -11,6 +11,7 @@ #include "content/browser/devtools/protocol/system_info_handler.h" #include "content/browser/devtools/protocol/tethering_handler.h" #include "content/browser/devtools/protocol/tracing_handler.h" +#include "content/browser/frame_host/frame_tree_node.h" namespace content { @@ -30,7 +31,9 @@ BrowserDevToolsAgentHost::BrowserDevToolsAgentHost( new devtools::tethering::TetheringHandler(socket_callback, tethering_task_runner)), tracing_handler_(new devtools::tracing::TracingHandler( - devtools::tracing::TracingHandler::Browser, GetIOContext())), + devtools::tracing::TracingHandler::Browser, + FrameTreeNode::kFrameTreeNodeInvalidId, + GetIOContext())), protocol_handler_(new DevToolsProtocolHandler(this)) { DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); dispatcher->SetIOHandler(io_handler_.get()); diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.cc b/chromium/content/browser/devtools/devtools_agent_host_impl.cc index df27d124201..033f2f18bd9 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.cc +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.cc @@ -235,10 +235,13 @@ void DevToolsAgentHostImpl::NotifyCallbacks( (*it)->Run(agent_host, attached); } -void DevToolsAgentHostImpl::Inspect(BrowserContext* browser_context) { +bool DevToolsAgentHostImpl::Inspect(BrowserContext* browser_context) { DevToolsManager* manager = DevToolsManager::GetInstance(); - if (manager->delegate()) + if (manager->delegate()) { manager->delegate()->Inspect(browser_context, this); + return true; + } + return false; } // DevToolsMessageChunkProcessor ----------------------------------------------- diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.h b/chromium/content/browser/devtools/devtools_agent_host_impl.h index f3b06246816..f70ebc5fc97 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.h +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.h @@ -35,7 +35,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost, virtual void Detach() = 0; // Opens the inspector for this host. - void Inspect(BrowserContext* browser_context); + bool Inspect(BrowserContext* browser_context); // DevToolsAgentHost implementation. void AttachClient(DevToolsAgentHostClient* client) override; diff --git a/chromium/content/browser/devtools/devtools_frame_trace_recorder.cc b/chromium/content/browser/devtools/devtools_frame_trace_recorder.cc index 618bbf8517c..d5756385810 100644 --- a/chromium/content/browser/devtools/devtools_frame_trace_recorder.cc +++ b/chromium/content/browser/devtools/devtools_frame_trace_recorder.cc @@ -78,7 +78,7 @@ void FrameCaptured(base::TimeTicks timestamp, const SkBitmap& bitmap, TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), "Screenshot", 1, timestamp.ToInternalValue(), - scoped_refptr( + scoped_ptr( new TraceableDevToolsScreenshot(bitmap))); } diff --git a/chromium/content/browser/devtools/devtools_frame_trace_recorder.h b/chromium/content/browser/devtools/devtools_frame_trace_recorder.h index 1d026676cf8..79c6f16145c 100644 --- a/chromium/content/browser/devtools/devtools_frame_trace_recorder.h +++ b/chromium/content/browser/devtools/devtools_frame_trace_recorder.h @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" namespace cc { diff --git a/chromium/content/browser/devtools/devtools_manager.h b/chromium/content/browser/devtools/devtools_manager.h index 57b9ea022b3..f803c1331d7 100644 --- a/chromium/content/browser/devtools/devtools_manager.h +++ b/chromium/content/browser/devtools/devtools_manager.h @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "content/common/content_export.h" #include "content/public/browser/devtools_manager_delegate.h" diff --git a/chromium/content/browser/devtools/devtools_netlog_observer.cc b/chromium/content/browser/devtools/devtools_netlog_observer.cc index 24448e330cf..6a2a3c45be0 100644 --- a/chromium/content/browser/devtools/devtools_netlog_observer.cc +++ b/chromium/content/browser/devtools/devtools_netlog_observer.cc @@ -138,8 +138,8 @@ void DevToolsNetLogObserver::OnAddURLRequestEntry( // several http requests (e.g. see http://crbug.com/80157). info->response_headers.clear(); - for (void* it = NULL; - response_headers->EnumerateHeaderLines(&it, &name, &value); ) { + for (size_t it = 0; + response_headers->EnumerateHeaderLines(&it, &name, &value);) { info->response_headers.push_back(std::make_pair(name, value)); } diff --git a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index 62f7a35aade..3f471114c64 100644 --- a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -282,7 +282,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, DISABLED_SynthesizePinchGesture) { ASSERT_DOUBLE_EQ(2.0, static_cast(old_height) / new_height); } -IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, SynthesizeScrollGesture) { +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, DISABLED_SynthesizeScrollGesture) { GURL test_url = GetTestUrl("devtools", "synthetic_gesture_tests.html"); NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); Attach(); @@ -306,7 +306,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, SynthesizeScrollGesture) { ASSERT_EQ(100, scroll_top); } -IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, SynthesizeTapGesture) { +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, DISABLED_SynthesizeTapGesture) { GURL test_url = GetTestUrl("devtools", "synthetic_gesture_tests.html"); NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); Attach(); @@ -344,7 +344,9 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, NavigationPreservesMessages) { scoped_ptr params(new base::DictionaryValue()); test_url = GetTestUrl("devtools", "navigation.html"); params->SetString("url", test_url.spec()); + TestNavigationObserver navigation_observer(shell()->web_contents()); SendCommand("Page.navigate", std::move(params), true); + navigation_observer.Wait(); bool enough_results = result_ids_.size() >= 2u; EXPECT_TRUE(enough_results); @@ -486,4 +488,30 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, ReloadBlankPage) { // Should not crash at this point. } +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, EvaluateInBlankPage) { + NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); + Attach(); + scoped_ptr params(new base::DictionaryValue()); + params->SetString("expression", "window"); + SendCommand("Runtime.evaluate", std::move(params), true); + bool wasThrown = true; + EXPECT_TRUE(result_->GetBoolean("wasThrown", &wasThrown)); + EXPECT_FALSE(wasThrown); +} + +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + EvaluateInBlankPageAfterNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL test_url = embedded_test_server()->GetURL("/devtools/navigation.html"); + NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 1); + Attach(); + NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); + scoped_ptr params(new base::DictionaryValue()); + params->SetString("expression", "window"); + SendCommand("Runtime.evaluate", std::move(params), true); + bool wasThrown = true; + EXPECT_TRUE(result_->GetBoolean("wasThrown", &wasThrown)); + EXPECT_FALSE(wasThrown); +} + } // namespace content diff --git a/chromium/content/browser/devtools/protocol/devtools_protocol_client.h b/chromium/content/browser/devtools/protocol/devtools_protocol_client.h index fe17c995f77..ba1a0d79c53 100644 --- a/chromium/content/browser/devtools/protocol/devtools_protocol_client.h +++ b/chromium/content/browser/devtools/protocol/devtools_protocol_client.h @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/values.h" namespace content { diff --git a/chromium/content/browser/devtools/protocol/devtools_protocol_handler_generator.py b/chromium/content/browser/devtools/protocol/devtools_protocol_handler_generator.py index df56ffe8a5c..2f244625c3a 100755 --- a/chromium/content/browser/devtools/protocol/devtools_protocol_handler_generator.py +++ b/chromium/content/browser/devtools/protocol/devtools_protocol_handler_generator.py @@ -568,6 +568,7 @@ def ResolveObject(json, mapping): mapping["storage_type"] = "scoped_ptr" mapping["raw_type"] = "base::DictionaryValue*" mapping["pass_template"] = tmpl_object_pass + mapping["init"] = " = nullptr" if "properties" in json: if not "declared_name" in mapping: mapping["declared_name"] = ("%s%s" % diff --git a/chromium/content/browser/devtools/protocol/emulation_handler.cc b/chromium/content/browser/devtools/protocol/emulation_handler.cc index f5945d71321..6b124e37131 100644 --- a/chromium/content/browser/devtools/protocol/emulation_handler.cc +++ b/chromium/content/browser/devtools/protocol/emulation_handler.cc @@ -23,6 +23,19 @@ using Response = DevToolsProtocolClient::Response; namespace { +blink::WebScreenOrientationType WebScreenOrientationTypeFromString( + const std::string& type) { + if (type == screen_orientation::kTypePortraitPrimary) + return blink::WebScreenOrientationPortraitPrimary; + if (type == screen_orientation::kTypePortraitSecondary) + return blink::WebScreenOrientationPortraitSecondary; + if (type == screen_orientation::kTypeLandscapePrimary) + return blink::WebScreenOrientationLandscapePrimary; + if (type == screen_orientation::kTypeLandscapeSecondary) + return blink::WebScreenOrientationLandscapeSecondary; + return blink::WebScreenOrientationUndefined; +} + ui::GestureProviderConfigType TouchEmulationConfigurationToType( const std::string& protocol_value) { ui::GestureProviderConfigType result = @@ -132,9 +145,11 @@ Response EmulationHandler::SetDeviceMetricsOverride( const int* screen_width, const int* screen_height, const int* position_x, - const int* position_y) { + const int* position_y, + const scoped_ptr& screen_orientation) { const static int max_size = 10000000; const static double max_scale = 10; + const static int max_orientation_angle = 360; if (!host_) return Response::InternalError("Could not connect to view"); @@ -168,6 +183,30 @@ Response EmulationHandler::SetDeviceMetricsOverride( base::DoubleToString(max_scale)); } + blink::WebScreenOrientationType orientationType = + blink::WebScreenOrientationUndefined; + int orientationAngle = 0; + if (screen_orientation) { + std::string orientationTypeString; + if (!screen_orientation->GetString("type", &orientationTypeString)) { + return Response::InvalidParams( + "Screen orientation type must be a string"); + } + orientationType = WebScreenOrientationTypeFromString(orientationTypeString); + if (orientationType == blink::WebScreenOrientationUndefined) + return Response::InvalidParams("Invalid screen orientation type value"); + + if (!screen_orientation->GetInteger("angle", &orientationAngle)) { + return Response::InvalidParams( + "Screen orientation angle must be a number"); + } + if (orientationAngle < 0 || orientationAngle >= max_orientation_angle) { + return Response::InvalidParams( + "Screen orientation angle must be non-negative, less than " + + base::IntToString(max_orientation_angle)); + } + } + blink::WebDeviceEmulationParams params; params.screenPosition = mobile ? blink::WebDeviceEmulationParams::Mobile : blink::WebDeviceEmulationParams::Desktop; @@ -182,6 +221,8 @@ Response EmulationHandler::SetDeviceMetricsOverride( params.offset = blink::WebFloatPoint( optional_offset_x ? *optional_offset_x : 0.f, optional_offset_y ? *optional_offset_y : 0.f); + params.screenOrientationType = orientationType; + params.screenOrientationAngle = orientationAngle; if (device_emulation_enabled_ && params == device_emulation_params_) return Response::OK(); diff --git a/chromium/content/browser/devtools/protocol/emulation_handler.h b/chromium/content/browser/devtools/protocol/emulation_handler.h index bcaeb0d0410..a5aec635500 100644 --- a/chromium/content/browser/devtools/protocol/emulation_handler.h +++ b/chromium/content/browser/devtools/protocol/emulation_handler.h @@ -39,18 +39,20 @@ class EmulationHandler { const std::string* configuration); Response CanEmulate(bool* result); - Response SetDeviceMetricsOverride(int width, - int height, - double device_scale_factor, - bool mobile, - bool fit_window, - const double* optional_scale, - const double* optional_offset_x, - const double* optional_offset_y, - const int* screen_widget, - const int* screen_height, - const int* position_x, - const int* position_y); + Response SetDeviceMetricsOverride( + int width, + int height, + double device_scale_factor, + bool mobile, + bool fit_window, + const double* optional_scale, + const double* optional_offset_x, + const double* optional_offset_y, + const int* screen_widget, + const int* screen_height, + const int* position_x, + const int* position_y, + const scoped_ptr& screen_orientation); Response ClearDeviceMetricsOverride(); private: diff --git a/chromium/content/browser/devtools/protocol/input_handler.cc b/chromium/content/browser/devtools/protocol/input_handler.cc index d9c17f3ee0e..08c3cf2cd36 100644 --- a/chromium/content/browser/devtools/protocol/input_handler.cc +++ b/chromium/content/browser/devtools/protocol/input_handler.cc @@ -69,8 +69,13 @@ void SetEventModifiers(blink::WebInputEvent* event, const int* modifiers) { } void SetEventTimestamp(blink::WebInputEvent* event, const double* timestamp) { - event->timeStampSeconds = - timestamp ? *timestamp : base::Time::Now().ToDoubleT(); + // Convert timestamp, in seconds since unix epoch, to an event timestamp + // which is time ticks since platform start time. + base::TimeTicks ticks = timestamp + ? base::TimeDelta::FromSecondsD(*timestamp) + + base::TimeTicks::UnixEpoch() + : base::TimeTicks::Now(); + event->timeStampSeconds = (ticks - base::TimeTicks()).InSecondsF(); } bool SetKeyboardEventText(blink::WebUChar* to, const std::string* from) { @@ -248,6 +253,7 @@ Response InputHandler::DispatchMouseEvent( event.globalX = x * page_scale_factor_; event.globalY = y * page_scale_factor_; event.clickCount = click_count ? *click_count : 0; + event.pointerType = blink::WebPointerProperties::PointerType::Mouse; if (!host_) return Response::ServerError("Could not connect to view"); @@ -296,6 +302,7 @@ Response InputHandler::EmulateTouchFromMouseEvent(const std::string& type, event->globalX = x; event->globalY = y; event->clickCount = click_count ? *click_count : 0; + event->pointerType = blink::WebPointerProperties::PointerType::Touch; if (!host_) return Response::ServerError("Could not connect to view"); diff --git a/chromium/content/browser/devtools/protocol/inspector_handler.cc b/chromium/content/browser/devtools/protocol/inspector_handler.cc index 31e9d1bf9c6..8e018a8477e 100644 --- a/chromium/content/browser/devtools/protocol/inspector_handler.cc +++ b/chromium/content/browser/devtools/protocol/inspector_handler.cc @@ -38,9 +38,12 @@ void InspectorHandler::TargetDetached(const std::string& reason) { Response InspectorHandler::Enable() { if (host_ && !host_->IsRenderFrameLive()) client_->TargetCrashed(TargetCrashedParams::Create()); - return Response::FallThrough(); + return Response::OK(); } +Response InspectorHandler::Disable() { + return Response::OK(); +} } // namespace inspector } // namespace devtools diff --git a/chromium/content/browser/devtools/protocol/inspector_handler.h b/chromium/content/browser/devtools/protocol/inspector_handler.h index 08c1e5e3efb..52325c48a69 100644 --- a/chromium/content/browser/devtools/protocol/inspector_handler.h +++ b/chromium/content/browser/devtools/protocol/inspector_handler.h @@ -29,6 +29,7 @@ class InspectorHandler { void TargetDetached(const std::string& reason); Response Enable(); + Response Disable(); private: scoped_ptr client_; diff --git a/chromium/content/browser/devtools/protocol/memory_handler.cc b/chromium/content/browser/devtools/protocol/memory_handler.cc index ef5cda2703a..9107fa93a4f 100644 --- a/chromium/content/browser/devtools/protocol/memory_handler.cc +++ b/chromium/content/browser/devtools/protocol/memory_handler.cc @@ -6,7 +6,7 @@ #include "base/memory/memory_pressure_listener.h" #include "base/strings/stringprintf.h" -#include "content/browser/memory/memory_pressure_controller.h" +#include "content/browser/memory/memory_pressure_controller_impl.h" namespace content { namespace devtools { @@ -18,7 +18,7 @@ MemoryHandler::~MemoryHandler() {} MemoryHandler::Response MemoryHandler::SetPressureNotificationsSuppressed( bool suppressed) { - content::MemoryPressureController::GetInstance() + content::MemoryPressureControllerImpl::GetInstance() ->SetPressureNotificationsSuppressedInAllProcesses(suppressed); return Response::OK(); } @@ -35,7 +35,7 @@ MemoryHandler::Response MemoryHandler::SimulatePressureNotification( "Invalid memory pressure level '%s'", level.c_str())); } - MemoryPressureController::GetInstance() + MemoryPressureControllerImpl::GetInstance() ->SimulatePressureNotificationInAllProcesses(parsed_level); return Response::OK(); } diff --git a/chromium/content/browser/devtools/protocol/network_handler.cc b/chromium/content/browser/devtools/protocol/network_handler.cc index d18d87a6b96..5163f19b853 100644 --- a/chromium/content/browser/devtools/protocol/network_handler.cc +++ b/chromium/content/browser/devtools/protocol/network_handler.cc @@ -15,8 +15,10 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/cert_store.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/site_instance.h" +#include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/content_client.h" @@ -133,8 +135,6 @@ class GetCookiesCommand { request_count_(0) { CookieListCallback got_cookies_callback = base::Bind( &GetCookiesCommand::GotCookiesForURL, base::Unretained(this)); - BrowserContext* browser_context = - frame_host->GetSiteInstance()->GetBrowserContext(); std::queue queue; queue.push(frame_host->frame_tree_node()); @@ -145,11 +145,12 @@ class GetCookiesCommand { // Only traverse nodes with the same local root. if (node->current_frame_host()->IsCrossProcessSubframe()) continue; - int process_id = node->current_frame_host()->GetProcess()->GetID(); ++request_count_; GetCookiesForURLOnUI( - browser_context->GetResourceContext(), - browser_context->GetRequestContextForRenderProcess(process_id), + frame_host->GetSiteInstance()->GetBrowserContext()-> + GetResourceContext(), + frame_host->GetProcess()->GetStoragePartition()-> + GetURLRequestContext(), node->current_url(), got_cookies_callback); @@ -233,7 +234,7 @@ void NetworkHandler::SendGetCookiesResponse( std::vector> cookies; for (size_t i = 0; i < cookie_list.size(); ++i) { const net::CanonicalCookie& cookie = cookie_list[i]; - cookies.push_back(Cookie::Create() + scoped_refptr devtools_cookie = Cookie::Create() ->set_name(cookie.Name()) ->set_value(cookie.Value()) ->set_domain(cookie.Domain()) @@ -242,7 +243,19 @@ void NetworkHandler::SendGetCookiesResponse( ->set_size(cookie.Name().length() + cookie.Value().length()) ->set_http_only(cookie.IsHttpOnly()) ->set_secure(cookie.IsSecure()) - ->set_session(!cookie.IsPersistent())); + ->set_session(!cookie.IsPersistent()); + + switch (cookie.SameSite()) { + case net::CookieSameSite::STRICT_MODE: + devtools_cookie->set_same_site(cookie::kSameSiteStrict); + break; + case net::CookieSameSite::LAX_MODE: + devtools_cookie->set_same_site(cookie::kSameSiteLax); + break; + case net::CookieSameSite::NO_RESTRICTION: + break; + } + cookies.push_back(devtools_cookie); } client_->SendGetCookiesResponse(command_id, GetCookiesResponse::Create()->set_cookies(cookies)); @@ -254,12 +267,9 @@ Response NetworkHandler::DeleteCookie( const std::string& url) { if (!host_) return Response::InternalError("Could not connect to view"); - BrowserContext* browser_context = - host_->GetSiteInstance()->GetBrowserContext(); - int process_id = host_->GetProcess()->GetID(); DeleteCookieOnUI( - browser_context->GetResourceContext(), - browser_context->GetRequestContextForRenderProcess(process_id), + host_->GetSiteInstance()->GetBrowserContext()->GetResourceContext(), + host_->GetProcess()->GetStoragePartition()->GetURLRequestContext(), GURL(url), cookie_name, base::Bind(&NetworkHandler::SendDeleteCookieResponse, diff --git a/chromium/content/browser/devtools/protocol/page_handler.cc b/chromium/content/browser/devtools/protocol/page_handler.cc index 7e79a7200cf..d4f6c3976f4 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.cc +++ b/chromium/content/browser/devtools/protocol/page_handler.cc @@ -200,7 +200,7 @@ Response PageHandler::Disable() { return Response::FallThrough(); } -Response PageHandler::Reload(const bool* ignoreCache, +Response PageHandler::Reload(const bool* bypassCache, const std::string* script_to_evaluate_on_load, const std::string* script_preprocessor) { WebContentsImpl* web_contents = GetWebContents(); @@ -210,7 +210,10 @@ Response PageHandler::Reload(const bool* ignoreCache, if (web_contents->IsCrashed() || (web_contents->GetController().GetVisibleEntry() && web_contents->GetController().GetVisibleEntry()->IsViewSourceMode())) { - web_contents->GetController().Reload(false); + if (bypassCache && *bypassCache) + web_contents->GetController().ReloadBypassingCache(false); + else + web_contents->GetController().Reload(false); return Response::OK(); } else { // Handle reload in renderer except for crashed and view source mode. @@ -357,6 +360,14 @@ Response PageHandler::SetColorPickerEnabled(bool enabled) { return Response::OK(); } +Response PageHandler::RequestAppBanner() { + WebContentsImpl* web_contents = GetWebContents(); + if (!web_contents) + return Response::InternalError("Could not connect to view"); + web_contents->GetDelegate()->RequestAppBannerFromDevTools(web_contents); + return Response::OK(); +} + WebContentsImpl* PageHandler::GetWebContents() { return host_ ? static_cast(WebContents::FromRenderFrameHost(host_)) : diff --git a/chromium/content/browser/devtools/protocol/page_handler.h b/chromium/content/browser/devtools/protocol/page_handler.h index 02b685a50d1..067d9587b32 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.h +++ b/chromium/content/browser/devtools/protocol/page_handler.h @@ -49,7 +49,7 @@ class PageHandler : public NotificationObserver { Response Enable(); Response Disable(); - Response Reload(const bool* ignoreCache, + Response Reload(const bool* bypassCache, const std::string* script_to_evaluate_on_load, const std::string* script_preprocessor = NULL); @@ -77,6 +77,7 @@ class PageHandler : public NotificationObserver { const std::string& security_origin); Response SetColorPickerEnabled(bool enabled); + Response RequestAppBanner(); private: WebContentsImpl* GetWebContents(); diff --git a/chromium/content/browser/devtools/protocol/service_worker_handler.cc b/chromium/content/browser/devtools/protocol/service_worker_handler.cc index a84091c276b..b2f5c2572b7 100644 --- a/chromium/content/browser/devtools/protocol/service_worker_handler.cc +++ b/chromium/content/browser/devtools/protocol/service_worker_handler.cc @@ -16,6 +16,7 @@ #include "content/browser/service_worker/service_worker_context_watcher.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_version.h" +#include "content/common/service_worker/service_worker_utils.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host.h" @@ -23,6 +24,7 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/push_event_payload.h" #include "content/public/common/push_messaging_status.h" #include "url/gurl.h" @@ -131,10 +133,7 @@ scoped_refptr CreateRegistrationDictionaryValue( base::Int64ToString(registration_info.registration_id)) ->set_scope_url(registration_info.pattern.spec()) ->set_is_deleted(registration_info.delete_flag == - ServiceWorkerRegistrationInfo::IS_DELETED) - ->set_force_update_on_page_load( - registration_info.force_update_on_page_load == - ServiceWorkerRegistrationInfo::IS_FORCED)); + ServiceWorkerRegistrationInfo::IS_DELETED)); return registration; } @@ -142,17 +141,27 @@ scoped_refptr GetMatchingServiceWorker( const ServiceWorkerDevToolsAgentHost::List& agent_hosts, const GURL& url) { scoped_refptr best_host; - std::string best_scope; + bool best_host_scope_matched = false; + int best_host_scope_length = 0; + for (auto host : agent_hosts) { if (host->GetURL().host_piece() != url.host_piece()) continue; - std::string path = host->GetURL().path(); - std::string file = host->GetURL().ExtractFileName(); - std::string scope = path.substr(0, path.length() - file.length()); - // Choose the latest, longest scope match worker. - if (scope.length() >= best_scope.length()) { + const bool scope_matched = + ServiceWorkerUtils::ScopeMatches(host->scope(), url); + const int scope_length = host->scope().spec().length(); + bool replace = false; + if (!best_host) + replace = true; + else if (best_host_scope_matched) + replace = scope_matched && scope_length >= best_host_scope_length; + else + replace = scope_matched || scope_length >= best_host_scope_length; + + if (replace) { best_host = host; - best_scope = scope; + best_host_scope_matched = scope_matched; + best_host_scope_length = scope_length; } } return best_host; @@ -176,11 +185,6 @@ ServiceWorkerDevToolsAgentHost::Map GetMatchingServiceWorkers( return result; } -bool CollectURLs(std::set* urls, FrameTreeNode* tree_node) { - urls->insert(tree_node->current_url()); - return false; -} - void StopServiceWorkerOnIO(scoped_refptr context, int64_t version_id) { if (content::ServiceWorkerVersion* version = @@ -246,6 +250,7 @@ void ServiceWorkerHandler::SetRenderFrameHost( render_frame_host_ = render_frame_host; // Do not call UpdateHosts yet, wait for load to commit. if (!render_frame_host) { + ClearForceUpdate(); context_ = nullptr; return; } @@ -268,8 +273,10 @@ void ServiceWorkerHandler::UpdateHosts() { urls_.clear(); BrowserContext* browser_context = nullptr; if (render_frame_host_) { - render_frame_host_->frame_tree_node()->frame_tree()->ForEach( - base::Bind(&CollectURLs, &urls_)); + for (FrameTreeNode* node : + render_frame_host_->frame_tree_node()->frame_tree()->Nodes()) + urls_.insert(node->current_url()); + browser_context = render_frame_host_->GetProcess()->GetBrowserContext(); } @@ -277,12 +284,12 @@ void ServiceWorkerHandler::UpdateHosts() { ServiceWorkerDevToolsAgentHost::Map new_hosts = GetMatchingServiceWorkers(browser_context, urls_); - for (auto pair : old_hosts) { + for (const auto& pair : old_hosts) { if (new_hosts.find(pair.first) == new_hosts.end()) ReportWorkerTerminated(pair.second.get()); } - for (auto pair : new_hosts) { + for (const auto& pair : new_hosts) { if (old_hosts.find(pair.first) == old_hosts.end()) ReportWorkerCreated(pair.second.get()); } @@ -301,11 +308,6 @@ Response ServiceWorkerHandler::Enable() { ServiceWorkerDevToolsManager::GetInstance()->AddObserver(this); - client_->DebugOnStartUpdated( - DebugOnStartUpdatedParams::Create()->set_debug_on_start( - ServiceWorkerDevToolsManager::GetInstance() - ->debug_service_worker_on_start())); - context_watcher_ = new ServiceWorkerContextWatcher( context_, base::Bind(&ServiceWorkerHandler::OnWorkerRegistrationUpdated, weak_factory_.GetWeakPtr()), @@ -325,6 +327,7 @@ Response ServiceWorkerHandler::Disable() { enabled_ = false; ServiceWorkerDevToolsManager::GetInstance()->RemoveObserver(this); + ClearForceUpdate(); for (const auto& pair : attached_hosts_) pair.second->DetachClient(); attached_hosts_.clear(); @@ -411,21 +414,11 @@ Response ServiceWorkerHandler::InspectWorker(const std::string& version_id) { return Response::OK(); } -Response ServiceWorkerHandler::SetDebugOnStart(bool debug_on_start) { - ServiceWorkerDevToolsManager::GetInstance() - ->set_debug_service_worker_on_start(debug_on_start); - return Response::OK(); -} - Response ServiceWorkerHandler::SetForceUpdateOnPageLoad( - const std::string& registration_id, bool force_update_on_page_load) { if (!context_) return CreateContextErrorResponse(); - int64_t id = kInvalidServiceWorkerRegistrationId; - if (!base::StringToInt64(registration_id, &id)) - return CreateInvalidVersionIdErrorResponse(); - context_->SetForceUpdateOnPageLoad(id, force_update_on_page_load); + context_->SetForceUpdateOnPageLoad(force_update_on_page_load); return Response::OK(); } @@ -440,9 +433,12 @@ Response ServiceWorkerHandler::DeliverPushMessage( int64_t id = 0; if (!base::StringToInt64(registration_id, &id)) return CreateInvalidVersionIdErrorResponse(); + PushEventPayload payload; + if (data.size() > 0) + payload.setData(data); BrowserContext::DeliverPushMessage( render_frame_host_->GetProcess()->GetBrowserContext(), GURL(origin), id, - data, base::Bind(&PushDeliveryNoOp)); + payload, base::Bind(&PushDeliveryNoOp)); return Response::OK(); } @@ -568,11 +564,6 @@ void ServiceWorkerHandler::WorkerDestroyed( UpdateHosts(); } -void ServiceWorkerHandler::DebugOnStartUpdated(bool debug_on_start) { - client_->DebugOnStartUpdated( - DebugOnStartUpdatedParams::Create()->set_debug_on_start(debug_on_start)); -} - void ServiceWorkerHandler::ReportWorkerCreated( ServiceWorkerDevToolsAgentHost* host) { if (host->IsAttached()) @@ -597,6 +588,11 @@ void ServiceWorkerHandler::ReportWorkerTerminated( attached_hosts_.erase(it); } +void ServiceWorkerHandler::ClearForceUpdate() { + if (context_) + context_->SetForceUpdateOnPageLoad(false); +} + } // namespace service_worker } // namespace devtools } // namespace content diff --git a/chromium/content/browser/devtools/protocol/service_worker_handler.h b/chromium/content/browser/devtools/protocol/service_worker_handler.h index 1508f5d375f..02dcd8c43f5 100644 --- a/chromium/content/browser/devtools/protocol/service_worker_handler.h +++ b/chromium/content/browser/devtools/protocol/service_worker_handler.h @@ -57,9 +57,7 @@ class ServiceWorkerHandler : public DevToolsAgentHostClient, Response StopWorker(const std::string& version_id); Response UpdateRegistration(const std::string& scope_url); Response InspectWorker(const std::string& version_id); - Response SetDebugOnStart(bool debug_on_start); - Response SetForceUpdateOnPageLoad(const std::string& registration_id, - bool force_update_on_page_load); + Response SetForceUpdateOnPageLoad(bool force_update_on_page_load); Response DeliverPushMessage(const std::string& origin, const std::string& registration_id, const std::string& data); @@ -71,7 +69,6 @@ class ServiceWorkerHandler : public DevToolsAgentHostClient, void WorkerCreated(ServiceWorkerDevToolsAgentHost* host) override; void WorkerReadyForInspection(ServiceWorkerDevToolsAgentHost* host) override; void WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) override; - void DebugOnStartUpdated(bool debug_on_start) override; private: // DevToolsAgentHostClient overrides. @@ -92,6 +89,7 @@ class ServiceWorkerHandler : public DevToolsAgentHostClient, const ServiceWorkerContextObserver::ErrorInfo& info); void OpenNewDevToolsWindow(int process_id, int devtools_agent_route_id); + void ClearForceUpdate(); scoped_refptr context_; scoped_ptr client_; diff --git a/chromium/content/browser/devtools/protocol/system_info_handler.cc b/chromium/content/browser/devtools/protocol/system_info_handler.cc index 27877b0bd98..5a9d53ad1e9 100644 --- a/chromium/content/browser/devtools/protocol/system_info_handler.cc +++ b/chromium/content/browser/devtools/protocol/system_info_handler.cc @@ -8,9 +8,12 @@ #include #include "base/bind.h" +#include "base/command_line.h" #include "content/browser/gpu/compositor_util.h" #include "content/public/browser/gpu_data_manager.h" +#include "gpu/config/gpu_feature_type.h" #include "gpu/config/gpu_info.h" +#include "gpu/config/gpu_switches.h" namespace content { namespace devtools { @@ -149,7 +152,9 @@ void SystemInfoHandler::SetClient(scoped_ptr client) { Response SystemInfoHandler::GetInfo(DevToolsCommandId command_id) { std::string reason; if (!GpuDataManager::GetInstance()->GpuAccessAllowed(&reason) || - GpuDataManager::GetInstance()->IsEssentialGpuInfoAvailable()) { + GpuDataManager::GetInstance()->IsEssentialGpuInfoAvailable() || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kGpuTestingNoCompleteInfoCollection)) { // The GpuDataManager already has all of the information needed to make // GPU-based blacklisting decisions. Post a task to give it to the // client asynchronously. diff --git a/chromium/content/browser/devtools/protocol/tethering_handler.cc b/chromium/content/browser/devtools/protocol/tethering_handler.cc index a58b534c368..1718261c355 100644 --- a/chromium/content/browser/devtools/protocol/tethering_handler.cc +++ b/chromium/content/browser/devtools/protocol/tethering_handler.cc @@ -7,6 +7,7 @@ #include "base/stl_util.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" +#include "net/base/ip_address.h" #include "net/base/net_errors.h" #include "net/socket/server_socket.h" #include "net/socket/stream_socket.h" @@ -18,8 +19,6 @@ namespace tethering { namespace { -const char kLocalhost[] = "127.0.0.1"; - const int kListenBacklog = 5; const int kBufferSize = 16 * 1024; @@ -171,11 +170,7 @@ class BoundSocket { bool Listen(uint16_t port) { port_ = port; - net::IPAddressNumber ip_number; - if (!net::ParseIPLiteralToNumber(kLocalhost, &ip_number)) - return false; - - net::IPEndPoint end_point(ip_number, port); + net::IPEndPoint end_point(net::IPAddress::IPv4Localhost(), port); int result = socket_->Listen(end_point, kListenBacklog); if (result < 0) return false; diff --git a/chromium/content/browser/devtools/protocol/tracing_handler.cc b/chromium/content/browser/devtools/protocol/tracing_handler.cc index 11cc18844ae..e7de4c753ce 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler.cc +++ b/chromium/content/browser/devtools/protocol/tracing_handler.cc @@ -8,14 +8,18 @@ #include "base/bind.h" #include "base/format_macros.h" +#include "base/json/json_writer.h" +#include "base/memory/ref_counted_memory.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event_impl.h" +#include "base/trace_event/tracing_agent.h" #include "components/tracing/trace_config_file.h" #include "content/browser/devtools/devtools_io_context.h" +#include "content/browser/tracing/tracing_controller_impl.h" namespace content { namespace devtools { @@ -27,6 +31,46 @@ namespace { const double kMinimumReportingInterval = 250.0; +const char kRecordModeParam[] = "record_mode"; + +// Convert from camel case to separator + lowercase. +std::string ConvertFromCamelCase(const std::string& in_str, char separator) { + std::string out_str; + out_str.reserve(in_str.size()); + for (const char& c : in_str) { + if (isupper(c)) { + out_str.push_back(separator); + out_str.push_back(tolower(c)); + } else { + out_str.push_back(c); + } + } + return out_str; +} + +scoped_ptr ConvertDictKeyStyle(const base::Value& value) { + const base::DictionaryValue* dict = nullptr; + if (value.GetAsDictionary(&dict)) { + scoped_ptr out_dict(new base::DictionaryValue()); + for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); + it.Advance()) { + out_dict->Set(ConvertFromCamelCase(it.key(), '_'), + ConvertDictKeyStyle(it.value())); + } + return std::move(out_dict); + } + + const base::ListValue* list = nullptr; + if (value.GetAsList(&list)) { + scoped_ptr out_list(new base::ListValue()); + for (const auto& value : *list) + out_list->Append(ConvertDictKeyStyle(*value)); + return std::move(out_list); + } + + return value.CreateDeepCopy(); +} + class DevToolsTraceSinkProxy : public TracingController::TraceDataSink { public: explicit DevToolsTraceSinkProxy(base::WeakPtr handler) @@ -85,9 +129,11 @@ class DevToolsStreamTraceSink : public TracingController::TraceDataSink { } // namespace TracingHandler::TracingHandler(TracingHandler::Target target, + int frame_tree_node_id, DevToolsIOContext* io_context) : target_(target), io_context_(io_context), + frame_tree_node_id_(frame_tree_node_id), did_initiate_recording_(false), return_as_stream_(false), weak_factory_(this) {} @@ -125,38 +171,46 @@ void TracingHandler::OnTraceToStreamComplete(const std::string& stream_handle) { TracingCompleteParams::Create()->set_stream(stream_handle)); } -Response TracingHandler::Start(DevToolsCommandId command_id, - const std::string* categories, - const std::string* options, - const double* buffer_usage_reporting_interval, - const std::string* transfer_mode) { +Response TracingHandler::Start( + DevToolsCommandId command_id, + const std::string* categories, + const std::string* options, + const double* buffer_usage_reporting_interval, + const std::string* transfer_mode, + const scoped_ptr& config) { if (IsTracing()) return Response::InternalError("Tracing is already started"); + if (config && (categories || options)) { + return Response::InternalError( + "Either trace config (preferred), or categories+options should be " + "specified, but not both."); + } + did_initiate_recording_ = true; return_as_stream_ = transfer_mode && *transfer_mode == start::kTransferModeReturnAsStream; - base::trace_event::TraceConfig trace_config( - categories ? *categories : std::string(), - options ? *options : std::string()); if (buffer_usage_reporting_interval) SetupTimer(*buffer_usage_reporting_interval); - // If inspected target is a render process Tracing.start will be handled by - // tracing agent in the renderer. - if (target_ == Renderer) { - TracingController::GetInstance()->StartTracing( - trace_config, - TracingController::StartTracingDoneCallback()); - return Response::FallThrough(); + base::trace_event::TraceConfig trace_config; + if (config) { + trace_config = GetTraceConfigFromDevToolsConfig(*config); + } else if (categories || options) { + trace_config = base::trace_event::TraceConfig( + categories ? *categories : std::string(), + options ? *options : std::string()); } + // If inspected target is a render process Tracing.start will be handled by + // tracing agent in the renderer. TracingController::GetInstance()->StartTracing( trace_config, base::Bind(&TracingHandler::OnRecordingEnabled, weak_factory_.GetWeakPtr(), command_id)); - return Response::OK(); + + return target_ == Renderer ? Response::FallThrough() : Response::OK(); } Response TracingHandler::End(DevToolsCommandId command_id) { @@ -188,7 +242,11 @@ Response TracingHandler::GetCategories(DevToolsCommandId command_id) { } void TracingHandler::OnRecordingEnabled(DevToolsCommandId command_id) { - client_->SendStartResponse(command_id, StartResponse::Create()); + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), + "TracingStartedInBrowser", TRACE_EVENT_SCOPE_THREAD, + "frameTreeNodeId", frame_tree_node_id_); + if (target_ != Renderer) + client_->SendStartResponse(command_id, StartResponse::Create()); } void TracingHandler::OnBufferUsage(float percent_full, @@ -233,6 +291,17 @@ void TracingHandler::OnMemoryDumpFinished(DevToolsCommandId command_id, ->set_success(success)); } +Response TracingHandler::RecordClockSyncMarker(const std::string& sync_id) { + if (!IsTracing()) + return Response::InternalError("Tracing is not started"); + + TracingControllerImpl::GetInstance()->RecordClockSyncMarker( + sync_id, + base::trace_event::TracingAgent::RecordClockSyncMarkerCallback()); + + return Response::OK(); +} + void TracingHandler::SetupTimer(double usage_reporting_interval) { if (usage_reporting_interval == 0) return; @@ -267,6 +336,21 @@ bool TracingHandler::IsStartupTracingActive() { TracingController::GetInstance()->IsTracing(); } +// static +base::trace_event::TraceConfig TracingHandler::GetTraceConfigFromDevToolsConfig( + const base::DictionaryValue& devtools_config) { + scoped_ptr value = ConvertDictKeyStyle(devtools_config); + DCHECK(value && value->IsType(base::Value::TYPE_DICTIONARY)); + scoped_ptr tracing_dict( + static_cast(value.release())); + + std::string mode; + if (tracing_dict->GetString(kRecordModeParam, &mode)) + tracing_dict->SetString(kRecordModeParam, ConvertFromCamelCase(mode, '-')); + + return base::trace_event::TraceConfig(*tracing_dict); +} + } // namespace tracing } // namespace devtools } // namespace content diff --git a/chromium/content/browser/devtools/protocol/tracing_handler.h b/chromium/content/browser/devtools/protocol/tracing_handler.h index f45d7e97c2e..be3e6e412ed 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler.h +++ b/chromium/content/browser/devtools/protocol/tracing_handler.h @@ -11,10 +11,12 @@ #include #include +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/trace_event/trace_event.h" #include "content/browser/devtools/protocol/devtools_protocol_dispatcher.h" +#include "content/common/content_export.h" #include "content/public/browser/tracing_controller.h" namespace base { @@ -34,7 +36,9 @@ class TracingHandler { typedef DevToolsProtocolClient::Response Response; enum Target { Browser, Renderer }; - TracingHandler(Target target, DevToolsIOContext* io_context); + TracingHandler(Target target, + int frame_tree_node_id, + DevToolsIOContext* io_context); virtual ~TracingHandler(); void SetClient(scoped_ptr client); @@ -49,10 +53,12 @@ class TracingHandler { const std::string* categories, const std::string* options, const double* buffer_usage_reporting_interval, - const std::string* transfer_mode); + const std::string* transfer_mode, + const scoped_ptr& config); Response End(DevToolsCommandId command_id); Response GetCategories(DevToolsCommandId command); Response RequestMemoryDump(DevToolsCommandId command_id); + Response RecordClockSyncMarker(const std::string& sync_id); bool did_initiate_recording() { return did_initiate_recording_; } @@ -70,16 +76,22 @@ class TracingHandler { const scoped_refptr& trace_data_sink); bool IsTracing() const; static bool IsStartupTracingActive(); + CONTENT_EXPORT static base::trace_event::TraceConfig + GetTraceConfigFromDevToolsConfig( + const base::DictionaryValue& devtools_config); scoped_ptr buffer_usage_poll_timer_; Target target_; scoped_ptr client_; DevToolsIOContext* io_context_; + int frame_tree_node_id_; bool did_initiate_recording_; bool return_as_stream_; base::WeakPtrFactory weak_factory_; + FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, + GetTraceConfigFromDevToolsConfig); DISALLOW_COPY_AND_ASSIGN(TracingHandler); }; diff --git a/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc b/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc new file mode 100644 index 00000000000..8182f45826f --- /dev/null +++ b/chromium/content/browser/devtools/protocol/tracing_handler_unittest.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 "base/json/json_reader.h" +#include "base/trace_event/trace_config.h" +#include "base/values.h" +#include "content/browser/devtools/protocol/tracing_handler.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace devtools { +namespace tracing { + +namespace { + +const char kCustomTraceConfigString[] = + "{" + "\"enable_argument_filter\":true," + "\"enable_sampling\":true," + "\"enable_systrace\":true," + "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," + "\"included_categories\":[\"included\"," + "\"inc_pattern*\"," + "\"disabled-by-default-cc\"," + "\"disabled-by-default-memory-infra\"]," + "\"memory_dump_config\":{" + "\"triggers\":[" + "{\"mode\":\"light\",\"periodic_interval_ms\":50}," + "{\"mode\":\"detailed\",\"periodic_interval_ms\":1000}" + "]" + "}," + "\"record_mode\":\"record-continuously\"," + "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" + "}"; + +const char kCustomTraceConfigStringDevToolsStyle[] = + "{" + "\"enableArgumentFilter\":true," + "\"enableSampling\":true," + "\"enableSystrace\":true," + "\"excludedCategories\":[\"excluded\",\"exc_pattern*\"]," + "\"includedCategories\":[\"included\"," + "\"inc_pattern*\"," + "\"disabled-by-default-cc\"," + "\"disabled-by-default-memory-infra\"]," + "\"memoryDumpConfig\":{" + "\"triggers\":[" + "{\"mode\":\"light\",\"periodicIntervalMs\":50}," + "{\"mode\":\"detailed\",\"periodicIntervalMs\":1000}" + "]" + "}," + "\"recordMode\":\"recordContinuously\"," + "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" + "}"; + +} + +TEST(TracingHandlerTest, GetTraceConfigFromDevToolsConfig) { + scoped_ptr value = base::JSONReader::Read( + kCustomTraceConfigStringDevToolsStyle); + scoped_ptr devtools_style_dict( + static_cast(value.release())); + + base::trace_event::TraceConfig trace_config = + TracingHandler::GetTraceConfigFromDevToolsConfig(*devtools_style_dict); + + EXPECT_STREQ(kCustomTraceConfigString, trace_config.ToString().c_str()); +} + +} // namespace tracing +} // namespace devtools +} // namespace content diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc index cb1be38ea98..d9a1a1c0b0d 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -24,6 +24,7 @@ #include "content/browser/devtools/protocol/tracing_handler.h" #include "content/browser/frame_host/navigation_handle_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/input/input_router_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/site_instance_impl.h" @@ -47,8 +48,6 @@ typedef std::vector Instances; namespace { base::LazyInstance::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; -bool browser_side_navigation = false; - static RenderFrameDevToolsAgentHost* FindAgentHost(RenderFrameHost* host) { if (g_instances == NULL) return NULL; @@ -60,19 +59,24 @@ static RenderFrameDevToolsAgentHost* FindAgentHost(RenderFrameHost* host) { return NULL; } -// Returns RenderFrameDevToolsAgentHost attached to any of RenderFrameHost -// instances associated with |web_contents| -static RenderFrameDevToolsAgentHost* FindAgentHost(WebContents* web_contents) { +static RenderFrameDevToolsAgentHost* FindAgentHost( + FrameTreeNode* frame_tree_node) { if (g_instances == NULL) return NULL; for (Instances::iterator it = g_instances.Get().begin(); it != g_instances.Get().end(); ++it) { - if ((*it)->GetWebContents() == web_contents) + if ((*it)->frame_tree_node() == frame_tree_node) return *it; } return NULL; } +static RenderFrameDevToolsAgentHost* FindAgentHost(WebContents* web_contents) { + if (!web_contents->GetMainFrame()) + return nullptr; + return FindAgentHost(web_contents->GetMainFrame()); +} + bool ShouldCreateDevToolsFor(RenderFrameHost* rfh) { return rfh->IsCrossProcessSubframe() || !rfh->GetParent(); } @@ -254,16 +258,11 @@ DevToolsAgentHost::GetOrCreateFor(RenderFrameHost* frame_host) { // static scoped_refptr DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) { - RenderFrameDevToolsAgentHost* result = FindAgentHost(web_contents); - if (!result) { - // TODO(dgozman): this check should not be necessary. See - // http://crbug.com/489664. - if (!web_contents->GetMainFrame()) - return nullptr; - result = new RenderFrameDevToolsAgentHost( - static_cast(web_contents->GetMainFrame())); - } - return result; + // TODO(dgozman): this check should not be necessary. See + // http://crbug.com/489664. + if (!web_contents->GetMainFrame()) + return nullptr; + return DevToolsAgentHost::GetOrCreateFor(web_contents->GetMainFrame()); } // static @@ -311,7 +310,7 @@ void RenderFrameDevToolsAgentHost::AddAllAgentHosts( void RenderFrameDevToolsAgentHost::OnCancelPendingNavigation( RenderFrameHost* pending, RenderFrameHost* current) { - if (browser_side_navigation) + if (IsBrowserSideNavigationEnabled()) return; RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(pending); @@ -331,6 +330,16 @@ void RenderFrameDevToolsAgentHost::OnBeforeNavigation( agent_host->AboutToNavigateRenderFrame(current, pending); } +// static +void RenderFrameDevToolsAgentHost::OnBeforeNavigation( + NavigationHandle* navigation_handle) { + FrameTreeNode* frame_tree_node = + static_cast(navigation_handle)->frame_tree_node(); + RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node); + if (agent_host) + agent_host->AboutToNavigate(navigation_handle); +} + RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost( RenderFrameHostImpl* host) : dom_handler_(new devtools::dom::DOMHandler()), @@ -344,15 +353,14 @@ RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost( new devtools::service_worker::ServiceWorkerHandler()), tracing_handler_(new devtools::tracing::TracingHandler( devtools::tracing::TracingHandler::Renderer, + host->GetFrameTreeNodeId(), GetIOContext())), emulation_handler_(nullptr), frame_trace_recorder_(nullptr), protocol_handler_(new DevToolsProtocolHandler(this)), current_frame_crashed_(false), pending_handle_(nullptr), - in_navigation_(0), frame_tree_node_(host->frame_tree_node()) { - browser_side_navigation = IsBrowserSideNavigationEnabled(); DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher(); dispatcher->SetDOMHandler(dom_handler_.get()); dispatcher->SetInputHandler(input_handler_.get()); @@ -454,8 +462,8 @@ bool RenderFrameDevToolsAgentHost::DispatchProtocolMessage( if (protocol_handler_->HandleOptionalMessage(session_id(), message, &call_id)) return true; - if (in_navigation_ > 0) { - DCHECK(browser_side_navigation); + if (!navigating_handles_.empty()) { + DCHECK(IsBrowserSideNavigationEnabled()); in_navigation_protocol_message_buffer_[call_id] = std::make_pair(session_id(), message); return true; @@ -480,8 +488,7 @@ void RenderFrameDevToolsAgentHost::OnClientAttached() { return; frame_trace_recorder_.reset(new DevToolsFrameTraceRecorder()); - -#if defined(OS_ANDROID) && !defined(USE_AURA) +#if defined(OS_ANDROID) power_save_blocker_.reset(static_cast( PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, @@ -520,54 +527,49 @@ RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() { g_instances.Get().erase(it); } -void RenderFrameDevToolsAgentHost::DidStartNavigation( - NavigationHandle* navigation_handle) { - if (!browser_side_navigation) - return; - if (!MatchesMyTreeNode(navigation_handle)) - return; - DCHECK(current_); - DCHECK(in_navigation_ >= 0); - ++in_navigation_; -} - void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation( NavigationHandle* navigation_handle) { // ReadyToCommitNavigation should only be called in PlzNavigate. - DCHECK(browser_side_navigation); - - if (MatchesMyTreeNode(navigation_handle) && in_navigation_ != 0) { - RenderFrameHostImpl* render_frame_host_impl = - static_cast( - navigation_handle->GetRenderFrameHost()); - if (current_->host() != render_frame_host_impl || current_frame_crashed_) { - SetPending(render_frame_host_impl); - pending_handle_ = navigation_handle; - } + DCHECK(IsBrowserSideNavigationEnabled()); + + // If the navigation is not tracked, return; + if (navigating_handles_.count(navigation_handle) == 0) + return; + + RenderFrameHostImpl* render_frame_host_impl = + static_cast( + navigation_handle->GetRenderFrameHost()); + if (current_->host() != render_frame_host_impl || current_frame_crashed_) { + SetPending(render_frame_host_impl); + pending_handle_ = navigation_handle; } } void RenderFrameDevToolsAgentHost::DidFinishNavigation( NavigationHandle* navigation_handle) { - if (!browser_side_navigation) + if (!IsBrowserSideNavigationEnabled()) + return; + + // If the navigation is not tracked, return; + if (navigating_handles_.count(navigation_handle) == 0) return; - if (MatchesMyTreeNode(navigation_handle) && in_navigation_ != 0) { - --in_navigation_; - DCHECK(in_navigation_ >= 0); - if (pending_handle_ == navigation_handle) { - // This navigation handle did set the pending FrameHostHolder. - DCHECK(pending_); - if (navigation_handle->HasCommitted()) { - DCHECK(pending_->host() == navigation_handle->GetRenderFrameHost()); - CommitPending(); - } else { - DiscardPending(); - } - pending_handle_ = nullptr; + // Now that the navigation is finished, remove the handle from the list of + // navigating handles. + navigating_handles_.erase(navigation_handle); + + if (pending_handle_ == navigation_handle) { + // This navigation handle did set the pending FrameHostHolder. + DCHECK(pending_); + if (navigation_handle->HasCommitted()) { + DCHECK(pending_->host() == navigation_handle->GetRenderFrameHost()); + CommitPending(); + } else { + DiscardPending(); } - DispatchBufferedProtocolMessagesIfNecessary(); + pending_handle_ = nullptr; } + DispatchBufferedProtocolMessagesIfNecessary(); if (navigation_handle->HasCommitted()) service_worker_handler_->UpdateHosts(); @@ -576,7 +578,7 @@ void RenderFrameDevToolsAgentHost::DidFinishNavigation( void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame( RenderFrameHost* old_host, RenderFrameHost* new_host) { - if (browser_side_navigation) + if (IsBrowserSideNavigationEnabled()) return; DCHECK(!pending_ || pending_->host() != old_host); @@ -588,10 +590,18 @@ void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame( SetPending(static_cast(new_host)); } +void RenderFrameDevToolsAgentHost::AboutToNavigate( + NavigationHandle* navigation_handle) { + if (!IsBrowserSideNavigationEnabled()) + return; + DCHECK(current_); + navigating_handles_.insert(navigation_handle); +} + void RenderFrameDevToolsAgentHost::RenderFrameHostChanged( RenderFrameHost* old_host, RenderFrameHost* new_host) { - if (browser_side_navigation) + if (IsBrowserSideNavigationEnabled()) return; DCHECK(!pending_ || pending_->host() != old_host); @@ -608,7 +618,7 @@ void RenderFrameDevToolsAgentHost::RenderFrameHostChanged( void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) { if (pending_ && pending_->host() == rfh) { - if (!browser_side_navigation) + if (!IsBrowserSideNavigationEnabled()) DiscardPending(); return; } @@ -671,24 +681,22 @@ bool RenderFrameDevToolsAgentHost::OnMessageReceived( bool RenderFrameDevToolsAgentHost::OnMessageReceived( const IPC::Message& message, RenderFrameHost* render_frame_host) { - if (message.type() != DevToolsClientMsg_DispatchOnInspectorFrontend::ID) + bool is_current = current_ && current_->host() == render_frame_host; + bool is_pending = pending_ && pending_->host() == render_frame_host; + if (!is_current && !is_pending) return false; if (!IsAttached()) return false; - - FrameHostHolder* holder = nullptr; - if (current_ && current_->host() == render_frame_host) - holder = current_.get(); - if (pending_ && pending_->host() == render_frame_host) - holder = pending_.get(); - if (!holder) - return false; - - DevToolsClientMsg_DispatchOnInspectorFrontend::Param param; - if (!DevToolsClientMsg_DispatchOnInspectorFrontend::Read(&message, ¶m)) - return false; - holder->ProcessChunkedMessageFromAgent(base::get<0>(param)); - return true; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(RenderFrameDevToolsAgentHost, message, + render_frame_host) + IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, + OnDispatchOnInspectorFrontend) + IPC_MESSAGE_HANDLER(DevToolsAgentHostMsg_RequestNewWindow, + OnRequestNewWindow) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; } void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() { @@ -713,7 +721,7 @@ void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame( RenderFrameHost* render_frame_host, const GURL& url, ui::PageTransition transition_type) { - if (browser_side_navigation) + if (IsBrowserSideNavigationEnabled()) return; if (pending_ && pending_->host() == render_frame_host) CommitPending(); @@ -726,7 +734,7 @@ void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad( int error_code, const base::string16& error_description, bool was_ignored_by_handler) { - if (browser_side_navigation) + if (IsBrowserSideNavigationEnabled()) return; if (pending_ && pending_->host() == render_frame_host) DiscardPending(); @@ -734,7 +742,8 @@ void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad( void RenderFrameDevToolsAgentHost:: DispatchBufferedProtocolMessagesIfNecessary() { - if (in_navigation_ == 0 && in_navigation_protocol_message_buffer_.size()) { + if (navigating_handles_.empty() && + in_navigation_protocol_message_buffer_.size()) { DCHECK(current_); for (const auto& pair : in_navigation_protocol_message_buffer_) { current_->DispatchProtocolMessage(pair.second.first, pair.first, @@ -768,7 +777,7 @@ void RenderFrameDevToolsAgentHost::DisconnectWebContents() { disconnected_->Detach(); frame_tree_node_ = nullptr; in_navigation_protocol_message_buffer_.clear(); - in_navigation_ = 0; + navigating_handles_.clear(); pending_handle_ = nullptr; WebContentsObserver::Observe(nullptr); } @@ -856,6 +865,33 @@ void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame( } } +void RenderFrameDevToolsAgentHost::OnDispatchOnInspectorFrontend( + RenderFrameHost* sender, + const DevToolsMessageChunk& message) { + if (current_ && current_->host() == sender) + current_->ProcessChunkedMessageFromAgent(message); + else if (pending_ && pending_->host() == sender) + pending_->ProcessChunkedMessageFromAgent(message); +} + +void RenderFrameDevToolsAgentHost::OnRequestNewWindow( + RenderFrameHost* sender, + int new_routing_id) { + RenderFrameHostImpl* frame_host = RenderFrameHostImpl::FromID( + sender->GetProcess()->GetID(), new_routing_id); + + bool success = false; + if (IsAttached() && sender->GetRoutingID() != new_routing_id && frame_host) { + scoped_refptr agent = + DevToolsAgentHost::GetOrCreateFor(frame_host); + success = static_cast(agent.get())-> + Inspect(agent->GetBrowserContext()); + } + + sender->Send(new DevToolsAgentMsg_RequestNewWindow_ACK( + sender->GetRoutingID(), success)); +} + bool RenderFrameDevToolsAgentHost::HasRenderFrameHost( RenderFrameHost* host) { return (current_ && current_->host() == host) || @@ -866,11 +902,4 @@ bool RenderFrameDevToolsAgentHost::IsChildFrame() { return current_ && current_->host()->GetParent(); } -bool RenderFrameDevToolsAgentHost::MatchesMyTreeNode( - NavigationHandle* navigation_handle) { - return frame_tree_node_ == - static_cast(navigation_handle) - ->frame_tree_node(); -} - } // namespace content diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h index 68409344de8..24d05358ad9 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h @@ -55,12 +55,15 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost RenderFrameHost* current); static void OnBeforeNavigation(RenderFrameHost* current, RenderFrameHost* pending); + static void OnBeforeNavigation(NavigationHandle* navigation_handle); void SynchronousSwapCompositorFrame( const cc::CompositorFrameMetadata& frame_metadata); bool HasRenderFrameHost(RenderFrameHost* host); + FrameTreeNode* frame_tree_node() { return frame_tree_node_; } + // DevTooolsAgentHost overrides. void DisconnectWebContents() override; void ConnectWebContents(WebContents* web_contents) override; @@ -90,7 +93,6 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost void InspectElement(int x, int y) override; // WebContentsObserver overrides. - void DidStartNavigation(NavigationHandle* navigation_handle) override; void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override; void DidFinishNavigation(NavigationHandle* navigation_handle) override; void RenderFrameHostChanged(RenderFrameHost* old_host, @@ -116,6 +118,7 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost void AboutToNavigateRenderFrame(RenderFrameHost* old_host, RenderFrameHost* new_host); + void AboutToNavigate(NavigationHandle* navigation_handle); void DispatchBufferedProtocolMessagesIfNecessary(); @@ -131,10 +134,12 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost void RenderFrameCrashed(); void OnSwapCompositorFrame(const IPC::Message& message); + void OnDispatchOnInspectorFrontend( + RenderFrameHost* sender, + const DevToolsMessageChunk& message); + void OnRequestNewWindow(RenderFrameHost* sender, int new_routing_id); void DestroyOnRenderFrameGone(); - bool MatchesMyTreeNode(NavigationHandle* navigation_handle); - class FrameHostHolder; scoped_ptr current_; @@ -166,9 +171,8 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost // Handle that caused the setting of pending_. NavigationHandle* pending_handle_; - // Navigation counter and queue for buffering protocol messages during a - // navigation. - int in_navigation_; + // List of handles currently navigating. + std::set navigating_handles_; // -> std::map> diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc index 3755d15c51e..33293d52e5b 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc @@ -109,6 +109,10 @@ int64_t ServiceWorkerDevToolsAgentHost::service_worker_version_id() const { return service_worker_->version_id(); } +GURL ServiceWorkerDevToolsAgentHost::scope() const { + return service_worker_->scope(); +} + bool ServiceWorkerDevToolsAgentHost::Matches( const ServiceWorkerIdentifier& other) { return service_worker_->Matches(other); diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h index 89b48b13b1f..a5f15ee7dc8 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h @@ -40,6 +40,7 @@ class ServiceWorkerDevToolsAgentHost : public WorkerDevToolsAgentHost { void OnAttachedStateChanged(bool attached) override; int64_t service_worker_version_id() const; + GURL scope() const; bool Matches(const ServiceWorkerIdentifier& other); diff --git a/chromium/content/browser/devtools/service_worker_devtools_manager.cc b/chromium/content/browser/devtools/service_worker_devtools_manager.cc index b94f1a9e3c2..6796b71db69 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_manager.cc +++ b/chromium/content/browser/devtools/service_worker_devtools_manager.cc @@ -16,19 +16,21 @@ ServiceWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier( const ServiceWorkerContextCore* context, base::WeakPtr context_weak, int64_t version_id, - const GURL& url) + const GURL& url, + const GURL& scope) : context_(context), context_weak_(context_weak), version_id_(version_id), - url_(url) {} + url_(url), + scope_(scope) {} ServiceWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier( const ServiceWorkerIdentifier& other) : context_(other.context_), context_weak_(other.context_weak_), version_id_(other.version_id_), - url_(other.url_) { -} + url_(other.url_), + scope_(other.scope_) {} ServiceWorkerDevToolsManager:: ServiceWorkerIdentifier::~ServiceWorkerIdentifier() { @@ -155,8 +157,6 @@ void ServiceWorkerDevToolsManager::RemoveObserver(Observer* observer) { void ServiceWorkerDevToolsManager::set_debug_service_worker_on_start( bool debug_on_start) { debug_service_worker_on_start_ = debug_on_start; - FOR_EACH_OBSERVER(Observer, observer_list_, - DebugOnStartUpdated(debug_on_start)); } ServiceWorkerDevToolsManager::ServiceWorkerDevToolsManager() diff --git a/chromium/content/browser/devtools/service_worker_devtools_manager.h b/chromium/content/browser/devtools/service_worker_devtools_manager.h index 306edd7d3f5..c2cd08e11cc 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_manager.h +++ b/chromium/content/browser/devtools/service_worker_devtools_manager.h @@ -35,7 +35,6 @@ class CONTENT_EXPORT ServiceWorkerDevToolsManager { virtual void WorkerReadyForInspection( ServiceWorkerDevToolsAgentHost* host) {} virtual void WorkerDestroyed(ServiceWorkerDevToolsAgentHost* host) {} - virtual void DebugOnStartUpdated(bool debug_on_start) {} protected: virtual ~Observer() {} @@ -47,7 +46,8 @@ class CONTENT_EXPORT ServiceWorkerDevToolsManager { const ServiceWorkerContextCore* context, base::WeakPtr context_weak, int64_t version_id, - const GURL& url); + const GURL& url, + const GURL& scope); ServiceWorkerIdentifier(const ServiceWorkerIdentifier& other); ~ServiceWorkerIdentifier(); @@ -59,12 +59,14 @@ class CONTENT_EXPORT ServiceWorkerDevToolsManager { } int64_t version_id() const { return version_id_; } GURL url() const { return url_; } + GURL scope() const { return scope_; } private: const ServiceWorkerContextCore* const context_; const base::WeakPtr context_weak_; const int64_t version_id_; const GURL url_; + const GURL scope_; }; // Returns the ServiceWorkerDevToolsManager singleton. diff --git a/chromium/content/browser/devtools/shared_worker_devtools_manager_unittest.cc b/chromium/content/browser/devtools/shared_worker_devtools_manager_unittest.cc index 98a715220b0..c6f86d5fc23 100644 --- a/chromium/content/browser/devtools/shared_worker_devtools_manager_unittest.cc +++ b/chromium/content/browser/devtools/shared_worker_devtools_manager_unittest.cc @@ -102,7 +102,7 @@ TEST_F(SharedWorkerDevToolsManagerTest, BasicTest) { SharedWorkerInstance instance1( GURL("http://example.com/w.js"), base::string16(), base::string16(), - blink::WebContentSecurityPolicyTypeReport, + blink::WebContentSecurityPolicyTypeReport, blink::WebAddressSpacePublic, browser_context_->GetResourceContext(), partition_id_, blink::WebSharedWorkerCreationContextTypeNonsecure); @@ -184,12 +184,12 @@ TEST_F(SharedWorkerDevToolsManagerTest, AttachTest) { SharedWorkerInstance instance1( GURL("http://example.com/w1.js"), base::string16(), base::string16(), - blink::WebContentSecurityPolicyTypeReport, + blink::WebContentSecurityPolicyTypeReport, blink::WebAddressSpacePublic, browser_context_->GetResourceContext(), partition_id_, blink::WebSharedWorkerCreationContextTypeNonsecure); SharedWorkerInstance instance2( GURL("http://example.com/w2.js"), base::string16(), base::string16(), - blink::WebContentSecurityPolicyTypeReport, + blink::WebContentSecurityPolicyTypeReport, blink::WebAddressSpacePublic, browser_context_->GetResourceContext(), partition_id_, blink::WebSharedWorkerCreationContextTypeNonsecure); @@ -267,7 +267,7 @@ TEST_F(SharedWorkerDevToolsManagerTest, AttachTest) { TEST_F(SharedWorkerDevToolsManagerTest, ReattachTest) { SharedWorkerInstance instance( GURL("http://example.com/w3.js"), base::string16(), base::string16(), - blink::WebContentSecurityPolicyTypeReport, + blink::WebContentSecurityPolicyTypeReport, blink::WebAddressSpacePublic, browser_context_->GetResourceContext(), partition_id_, blink::WebSharedWorkerCreationContextTypeNonsecure); scoped_ptr client_host(new TestDevToolsClientHost()); diff --git a/chromium/content/browser/devtools/site_per_process_devtools_browsertest.cc b/chromium/content/browser/devtools/site_per_process_devtools_browsertest.cc index cfa2b4a1042..e3c3cf222bc 100644 --- a/chromium/content/browser/devtools/site_per_process_devtools_browsertest.cc +++ b/chromium/content/browser/devtools/site_per_process_devtools_browsertest.cc @@ -23,7 +23,7 @@ class SitePerProcessDevToolsBrowserTest class TestClient: public DevToolsAgentHostClient { public: - TestClient() : closed_(false) {} + TestClient() : closed_(false), waiting_for_reply_(false) {} ~TestClient() override {} bool closed() { return closed_; } @@ -31,6 +31,10 @@ class TestClient: public DevToolsAgentHostClient { void DispatchProtocolMessage( DevToolsAgentHost* agent_host, const std::string& message) override { + if (waiting_for_reply_) { + waiting_for_reply_ = false; + base::MessageLoop::current()->QuitNow(); + } } void AgentHostClosed( @@ -39,8 +43,14 @@ class TestClient: public DevToolsAgentHostClient { closed_ = true; } + void WaitForReply() { + waiting_for_reply_ = true; + base::MessageLoop::current()->Run(); + } + private: bool closed_; + bool waiting_for_reply_; }; // Fails on Android, http://crbug.com/464993. @@ -90,10 +100,20 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, EXPECT_EQ(DevToolsAgentHost::TYPE_FRAME, list[1]->GetType()); EXPECT_EQ(cross_site_url.spec(), list[1]->GetURL().spec()); - // Attaching to child frame. + // Attaching to both agent hosts. scoped_refptr child_host = list[1]; - TestClient client; - child_host->AttachClient(&client); + TestClient child_client; + child_host->AttachClient(&child_client); + scoped_refptr parent_host = list[0]; + TestClient parent_client; + parent_host->AttachClient(&parent_client); + + // Send message to parent and child frames and get result back. + char message[] = "{\"id\": 0, \"method\": \"incorrect.method\"}"; + child_host->DispatchProtocolMessage(message); + child_client.WaitForReply(); + parent_host->DispatchProtocolMessage(message); + parent_client.WaitForReply(); // Load back same-site page into iframe. NavigateFrameToURL(root->child_at(0), http_url); @@ -102,9 +122,12 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, EXPECT_EQ(1U, list.size()); EXPECT_EQ(DevToolsAgentHost::TYPE_WEB_CONTENTS, list[0]->GetType()); EXPECT_EQ(main_url.spec(), list[0]->GetURL().spec()); - EXPECT_TRUE(client.closed()); + EXPECT_TRUE(child_client.closed()); child_host->DetachClient(); child_host = nullptr; + EXPECT_FALSE(parent_client.closed()); + parent_host->DetachClient(); + parent_host = nullptr; } IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, AgentHostForFrames) { @@ -145,4 +168,36 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, AgentHostForFrames) { EXPECT_NE(page_agent.get(), child_frame_agent.get()); } +IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsBrowserTest, + AgentHostForPageEqualsOneForMainFrame) { + host_resolver()->AddRule("*", "127.0.0.1"); + GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); + NavigateToURL(shell(), main_url); + + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = + static_cast(shell()->web_contents())-> + GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + + // Load cross-site page into iframe. + GURL::Replacements replace_host; + GURL cross_site_url(embedded_test_server()->GetURL("/title2.html")); + replace_host.SetHostStr("foo.com"); + cross_site_url = cross_site_url.ReplaceComponents(replace_host); + NavigateFrameToURL(child, cross_site_url); + + // First ask for child frame, then for main frame. + scoped_refptr child_frame_agent = + DevToolsAgentHost::GetOrCreateFor(child->current_frame_host()); + scoped_refptr main_frame_agent = + DevToolsAgentHost::GetOrCreateFor(root->current_frame_host()); + EXPECT_NE(main_frame_agent.get(), child_frame_agent.get()); + + // Agent for web contents should be the the main frame's one. + scoped_refptr page_agent = + DevToolsAgentHost::GetOrCreateFor(shell()->web_contents()); + EXPECT_EQ(page_agent.get(), main_frame_agent.get()); +} + } // namespace content diff --git a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc index b0c1c63d65f..9fc8c417dc9 100644 --- a/chromium/content/browser/dom_storage/dom_storage_browsertest.cc +++ b/chromium/content/browser/dom_storage/dom_storage_browsertest.cc @@ -9,7 +9,6 @@ #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" -#include "net/base/net_util.h" namespace content { diff --git a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc index db486f35e36..b0ec4d5ab65 100644 --- a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc +++ b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc @@ -11,15 +11,23 @@ #include "base/bind_helpers.h" #include "base/files/file_path.h" #include "base/location.h" +#include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" +#include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" +#include "components/filesystem/public/interfaces/directory.mojom.h" +#include "components/leveldb/public/interfaces/leveldb.mojom.h" +#include "components/profile_service/public/interfaces/profile.mojom.h" #include "content/browser/dom_storage/dom_storage_area.h" #include "content/browser/dom_storage/dom_storage_context_impl.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" +#include "content/browser/leveldb_wrapper_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/local_storage_usage_info.h" +#include "content/public/browser/mojo_app_connection.h" #include "content/public/browser/session_storage_usage_info.h" +#include "mojo/common/common_type_converters.h" namespace content { namespace { @@ -65,9 +73,175 @@ void GetSessionStorageUsageHelper( } // namespace +// Used for mojo-based LocalStorage implementation (behind --mojo-local-storage +// for now). +class DOMStorageContextWrapper::MojoState { + public: + MojoState(const std::string& mojo_user_id, const base::FilePath& subdirectory) + : mojo_user_id_(mojo_user_id), + subdirectory_(subdirectory), + connection_state_(NO_CONNECTION), + weak_ptr_factory_(this) {} + + void OpenLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request); + + private: + void OnLevelDDWrapperHasNoBindings(const url::Origin& origin) { + DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); + level_db_wrappers_.erase(origin); + } + + // Part of our asynchronous directory opening called from OpenLocalStorage(). + void OnDirectoryOpened(filesystem::FileError err); + void OnDatabaseOpened(leveldb::DatabaseError status); + + // The (possibly delayed) implementation of OpenLocalStorage(). Can be called + // directly from that function, or through |on_database_open_callbacks_|. + void BindLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request); + + // Maps between an origin and its prefixed LevelDB view. + std::map> level_db_wrappers_; + + std::string mojo_user_id_; + base::FilePath subdirectory_; + + enum ConnectionState { + NO_CONNECTION, + CONNECTION_IN_PROGRESS, + CONNECTION_FINISHED + } connection_state_; + + scoped_ptr profile_app_connection_; + profile::ProfileServicePtr profile_service_; + filesystem::DirectoryPtr directory_; + + leveldb::LevelDBServicePtr leveldb_service_; + leveldb::LevelDBDatabasePtr database_; + + std::vector on_database_opened_callbacks_; + + base::WeakPtrFactory weak_ptr_factory_; +}; + +void DOMStorageContextWrapper::MojoState::OpenLocalStorage( + const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request) { + // If we don't have a filesystem_connection_, we'll need to establish one. + if (connection_state_ == NO_CONNECTION) { + profile_app_connection_ = MojoAppConnection::Create( + mojo_user_id_, "mojo:profile", kBrowserMojoAppUrl); + + connection_state_ = CONNECTION_IN_PROGRESS; + + if (!subdirectory_.empty()) { + // We were given a subdirectory to write to. Get it and use a disk backed + // database. + profile_app_connection_->GetInterface(&profile_service_); + profile_service_->GetSubDirectory( + mojo::String::From(subdirectory_.AsUTF8Unsafe()), + GetProxy(&directory_), + base::Bind(&MojoState::OnDirectoryOpened, + weak_ptr_factory_.GetWeakPtr())); + } else { + // We were not given a subdirectory. Use a memory backed database. + profile_app_connection_->GetInterface(&leveldb_service_); + leveldb_service_->OpenInMemory( + GetProxy(&database_), + base::Bind(&MojoState::OnDatabaseOpened, + weak_ptr_factory_.GetWeakPtr())); + } + } + + if (connection_state_ == CONNECTION_IN_PROGRESS) { + // Queue this OpenLocalStorage call for when we have a level db pointer. + on_database_opened_callbacks_.push_back( + base::Bind(&MojoState::BindLocalStorage, weak_ptr_factory_.GetWeakPtr(), + origin, base::Passed(&observer), base::Passed(&request))); + return; + } + + BindLocalStorage(origin, std::move(observer), std::move(request)); +} + +void DOMStorageContextWrapper::MojoState::OnDirectoryOpened( + filesystem::FileError err) { + if (err != filesystem::FileError::OK) { + // We failed to open the directory; continue with startup so that we create + // the |level_db_wrappers_|. + OnDatabaseOpened(leveldb::DatabaseError::IO_ERROR); + return; + } + + // Now that we have a directory, connect to the LevelDB service and get our + // database. + profile_app_connection_->GetInterface(&leveldb_service_); + + leveldb_service_->Open( + std::move(directory_), "leveldb", GetProxy(&database_), + base::Bind(&MojoState::OnDatabaseOpened, weak_ptr_factory_.GetWeakPtr())); +} + +void DOMStorageContextWrapper::MojoState::OnDatabaseOpened( + leveldb::DatabaseError status) { + if (status != leveldb::DatabaseError::OK) { + // If we failed to open the database, reset the service object so we pass + // null pointers to our wrappers. + database_.reset(); + leveldb_service_.reset(); + } + + // We no longer need the profile service; we've either transferred + // |directory_| to the leveldb service, or we got a file error and no more is + // possible. + directory_.reset(); + profile_service_.reset(); + + // |leveldb_| should be known to either be valid or invalid by now. Run our + // delayed bindings. + connection_state_ = CONNECTION_FINISHED; + for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) + on_database_opened_callbacks_[i].Run(); + on_database_opened_callbacks_.clear(); +} + +void DOMStorageContextWrapper::MojoState::BindLocalStorage( + const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request) { + auto found = level_db_wrappers_.find(origin); + if (found == level_db_wrappers_.end()) { + level_db_wrappers_[origin] = make_scoped_ptr(new LevelDBWrapperImpl( + database_.get(), + origin.Serialize(), + kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, + base::Bind(&MojoState::OnLevelDDWrapperHasNoBindings, + base::Unretained(this), + origin))); + found = level_db_wrappers_.find(origin); + } + + found->second->Bind(std::move(request)); + found->second->AddObserver(std::move(observer)); +} + DOMStorageContextWrapper::DOMStorageContextWrapper( - const base::FilePath& data_path, + const std::string& mojo_user_id, + const base::FilePath& profile_path, + const base::FilePath& local_partition_path, storage::SpecialStoragePolicy* special_storage_policy) { + base::FilePath storage_dir; + if (!profile_path.empty()) + storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory); + mojo_state_.reset(new MojoState(mojo_user_id, storage_dir)); + + base::FilePath data_path; + if (!profile_path.empty()) + data_path = profile_path.Append(local_partition_path); base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool(); context_ = new DOMStorageContextImpl( data_path.empty() ? data_path @@ -83,8 +257,7 @@ DOMStorageContextWrapper::DOMStorageContextWrapper( .get())); } -DOMStorageContextWrapper::~DOMStorageContextWrapper() { -} +DOMStorageContextWrapper::~DOMStorageContextWrapper() {} void DOMStorageContextWrapper::GetLocalStorageUsage( const GetLocalStorageUsageCallback& callback) { @@ -92,7 +265,8 @@ void DOMStorageContextWrapper::GetLocalStorageUsage( context_->task_runner()->PostShutdownBlockingTask( FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE, base::Bind(&GetLocalStorageUsageHelper, - base::ThreadTaskRunnerHandle::Get(), context_, callback)); + base::RetainedRef(base::ThreadTaskRunnerHandle::Get()), + base::RetainedRef(context_), callback)); } void DOMStorageContextWrapper::GetSessionStorageUsage( @@ -101,7 +275,8 @@ void DOMStorageContextWrapper::GetSessionStorageUsage( context_->task_runner()->PostShutdownBlockingTask( FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE, base::Bind(&GetSessionStorageUsageHelper, - base::ThreadTaskRunnerHandle::Get(), context_, callback)); + base::RetainedRef(base::ThreadTaskRunnerHandle::Get()), + base::RetainedRef(context_), callback)); } void DOMStorageContextWrapper::DeleteLocalStorage(const GURL& origin) { @@ -153,6 +328,7 @@ void DOMStorageContextWrapper::SetForceKeepSessionState() { void DOMStorageContextWrapper::Shutdown() { DCHECK(context_.get()); + mojo_state_.reset(); context_->task_runner()->PostShutdownBlockingTask( FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE, @@ -166,4 +342,12 @@ void DOMStorageContextWrapper::Flush() { base::Bind(&DOMStorageContextImpl::Flush, context_)); } +void DOMStorageContextWrapper::OpenLocalStorage( + const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request) { + mojo_state_->OpenLocalStorage( + origin, std::move(observer), std::move(request)); +} + } // namespace content diff --git a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h index 53033447ca1..56196a1f997 100644 --- a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h +++ b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.h @@ -5,12 +5,15 @@ #ifndef CONTENT_BROWSER_DOM_STORAGE_DOM_STORAGE_CONTEXT_WRAPPER_H_ #define CONTENT_BROWSER_DOM_STORAGE_DOM_STORAGE_CONTEXT_WRAPPER_H_ +#include #include #include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/common/content_export.h" +#include "content/common/storage_partition_service.mojom.h" #include "content/public/browser/dom_storage_context.h" +#include "url/origin.h" namespace base { class FilePath; @@ -23,16 +26,19 @@ class SpecialStoragePolicy; namespace content { class DOMStorageContextImpl; +class LevelDBWrapperImpl; -// This is owned by BrowserContext (aka Profile) and encapsulates all -// per-profile dom storage state. +// This is owned by Storage Partition and encapsulates all its dom storage +// state. class CONTENT_EXPORT DOMStorageContextWrapper : NON_EXPORTED_BASE(public DOMStorageContext), public base::RefCountedThreadSafe { public: // If |data_path| is empty, nothing will be saved to disk. DOMStorageContextWrapper( + const std::string& mojo_user_id, const base::FilePath& data_path, + const base::FilePath& local_partition_path, storage::SpecialStoragePolicy* special_storage_policy); // DOMStorageContext implementation. @@ -58,6 +64,11 @@ class CONTENT_EXPORT DOMStorageContextWrapper : void Flush(); + // See mojom::StoragePartitionService interface. + void OpenLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, + mojom::LevelDBWrapperRequest request); + private: friend class DOMStorageMessageFilter; // for access to context() friend class SessionStorageNamespaceImpl; // ditto @@ -66,6 +77,11 @@ class CONTENT_EXPORT DOMStorageContextWrapper : ~DOMStorageContextWrapper() override; DOMStorageContextImpl* context() const { return context_.get(); } + // An inner class to keep all mojo-ish details together and not bleed them + // through the public interface. + class MojoState; + scoped_ptr mojo_state_; + scoped_refptr context_; DISALLOW_IMPLICIT_CONSTRUCTORS(DOMStorageContextWrapper); diff --git a/chromium/content/browser/dom_storage/dom_storage_host.cc b/chromium/content/browser/dom_storage/dom_storage_host.cc index 14d288d8653..3db7d5a15ec 100644 --- a/chromium/content/browser/dom_storage/dom_storage_host.cc +++ b/chromium/content/browser/dom_storage/dom_storage_host.cc @@ -153,6 +153,8 @@ DOMStorageNamespace* DOMStorageHost::GetNamespace(int connection_id) { // NamespaceAndArea DOMStorageHost::NamespaceAndArea::NamespaceAndArea() {} +DOMStorageHost::NamespaceAndArea::NamespaceAndArea( + const NamespaceAndArea& other) = default; DOMStorageHost::NamespaceAndArea::~NamespaceAndArea() {} } // namespace content diff --git a/chromium/content/browser/dom_storage/dom_storage_host.h b/chromium/content/browser/dom_storage/dom_storage_host.h index 9b79406cbd2..d53bd154eec 100644 --- a/chromium/content/browser/dom_storage/dom_storage_host.h +++ b/chromium/content/browser/dom_storage/dom_storage_host.h @@ -57,6 +57,7 @@ class CONTENT_EXPORT DOMStorageHost { scoped_refptr namespace_; scoped_refptr area_; NamespaceAndArea(); + NamespaceAndArea(const NamespaceAndArea& other); ~NamespaceAndArea(); }; typedef std::map AreaMap; diff --git a/chromium/content/browser/dom_storage/dom_storage_namespace.cc b/chromium/content/browser/dom_storage/dom_storage_namespace.cc index 25e44e53e3f..a8681b6e6c2 100644 --- a/chromium/content/browser/dom_storage/dom_storage_namespace.cc +++ b/chromium/content/browser/dom_storage/dom_storage_namespace.cc @@ -195,6 +195,8 @@ DOMStorageNamespace::AreaHolder::AreaHolder( : area_(area), open_count_(count) { } +DOMStorageNamespace::AreaHolder::AreaHolder(const AreaHolder& other) = default; + DOMStorageNamespace::AreaHolder::~AreaHolder() { } diff --git a/chromium/content/browser/dom_storage/dom_storage_namespace.h b/chromium/content/browser/dom_storage/dom_storage_namespace.h index a9f65693fe8..238b7ca554e 100644 --- a/chromium/content/browser/dom_storage/dom_storage_namespace.h +++ b/chromium/content/browser/dom_storage/dom_storage_namespace.h @@ -86,6 +86,7 @@ class CONTENT_EXPORT DOMStorageNamespace int open_count_; AreaHolder(); AreaHolder(DOMStorageArea* area, int count); + AreaHolder(const AreaHolder& other); ~AreaHolder(); }; typedef std::map AreaMap; diff --git a/chromium/content/browser/dom_storage/session_storage_database.cc b/chromium/content/browser/dom_storage/session_storage_database.cc index ec246164e34..0aea7c7cc34 100644 --- a/chromium/content/browser/dom_storage/session_storage_database.cc +++ b/chromium/content/browser/dom_storage/session_storage_database.cc @@ -383,6 +383,9 @@ leveldb::Status SessionStorageDatabase::TryToOpen(leveldb::DB** db) { options.max_open_files = 0; // Use minimum. options.create_if_missing = true; options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; + // Default write_buffer_size is 4 MB but that might leave a 3.999 + // memory allocation in RAM from a log file recovery. + options.write_buffer_size = 64 * 1024; return leveldb::DB::Open(options, file_path_.AsUTF8Unsafe(), db); } diff --git a/chromium/content/browser/download/OWNERS b/chromium/content/browser/download/OWNERS index 9e1fc0676f3..27f3217e697 100644 --- a/chromium/content/browser/download/OWNERS +++ b/chromium/content/browser/download/OWNERS @@ -1,4 +1,2 @@ -ahendrickson@chromium.org asanka@chromium.org -phajdan.jr@chromium.org rdsmith@chromium.org diff --git a/chromium/content/browser/download/base_file.cc b/chromium/content/browser/download/base_file.cc index 8fda8de6a5c..74072bb1f72 100644 --- a/chromium/content/browser/download/base_file.cc +++ b/chromium/content/browser/download/base_file.cc @@ -25,38 +25,8 @@ namespace content { -// This will initialize the entire array to zero. -const unsigned char BaseFile::kEmptySha256Hash[] = { 0 }; - -BaseFile::BaseFile(const base::FilePath& full_path, - const GURL& source_url, - const GURL& referrer_url, - int64_t received_bytes, - bool calculate_hash, - const std::string& hash_state_bytes, - base::File file, - const net::BoundNetLog& bound_net_log) - : full_path_(full_path), - source_url_(source_url), - referrer_url_(referrer_url), - file_(std::move(file)), - bytes_so_far_(received_bytes), - start_tick_(base::TimeTicks::Now()), - calculate_hash_(calculate_hash), - detached_(false), - bound_net_log_(bound_net_log) { - memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length); - if (calculate_hash_) { - secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); - if ((bytes_so_far_ > 0) && // Not starting at the beginning. - (!IsEmptyHash(hash_state_bytes))) { - base::Pickle hash_state(hash_state_bytes.c_str(), - hash_state_bytes.size()); - base::PickleIterator data_iterator(hash_state); - secure_hash_->Deserialize(&data_iterator); - } - } -} +BaseFile::BaseFile(const net::BoundNetLog& bound_net_log) + : bound_net_log_(bound_net_log) {} BaseFile::~BaseFile() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); @@ -67,11 +37,16 @@ BaseFile::~BaseFile() { } DownloadInterruptReason BaseFile::Initialize( - const base::FilePath& default_directory) { + const base::FilePath& full_path, + const base::FilePath& default_directory, + base::File file, + int64_t bytes_so_far, + const std::string& hash_so_far, + scoped_ptr hash_state) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(!detached_); - if (full_path_.empty()) { + if (full_path.empty()) { base::FilePath initial_directory(default_directory); base::FilePath temp_file; if (initial_directory.empty()) { @@ -87,9 +62,15 @@ DownloadInterruptReason BaseFile::Initialize( DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); } full_path_ = temp_file; + } else { + full_path_ = full_path; } - return Open(); + bytes_so_far_ = bytes_so_far; + secure_hash_ = std::move(hash_state); + file_ = std::move(file); + + return Open(hash_so_far); } DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, @@ -134,7 +115,7 @@ DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, RecordDownloadWriteSize(data_len); RecordDownloadWriteLoopCount(write_count); - if (calculate_hash_) + if (secure_hash_) secure_hash_->Update(data, data_len); return DOWNLOAD_INTERRUPT_REASON_NONE; @@ -170,7 +151,7 @@ DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) { // reason. DownloadInterruptReason open_result = DOWNLOAD_INTERRUPT_REASON_NONE; if (was_in_progress) - open_result = Open(); + open_result = Open(std::string()); bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_FILE_RENAMED); return rename_result == DOWNLOAD_INTERRUPT_REASON_NONE ? open_result @@ -198,92 +179,127 @@ void BaseFile::Cancel() { Detach(); } -void BaseFile::Finish() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - - if (calculate_hash_) - secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length); - Close(); -} - -void BaseFile::FinishWithError() { +scoped_ptr BaseFile::Finish() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); Close(); -} - -void BaseFile::SetClientGuid(const std::string& guid) { - client_guid_ = guid; + return std::move(secure_hash_); } // OS_WIN, OS_MACOSX and OS_LINUX have specialized implementations. #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX) -DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { +DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url) { return DOWNLOAD_INTERRUPT_REASON_NONE; } #endif -bool BaseFile::GetHash(std::string* hash) { - DCHECK(!detached_); - hash->assign(reinterpret_cast(sha256_hash_), - sizeof(sha256_hash_)); - return (calculate_hash_ && !in_progress()); +std::string BaseFile::DebugString() const { + return base::StringPrintf( + "{ " + " full_path_ = \"%" PRFilePath + "\"" + " bytes_so_far_ = %" PRId64 " detached_ = %c }", + full_path_.value().c_str(), + bytes_so_far_, + detached_ ? 'T' : 'F'); } -std::string BaseFile::GetHashState() { - if (!calculate_hash_) - return std::string(); +DownloadInterruptReason BaseFile::CalculatePartialHash( + const std::string& hash_to_expect) { + secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); - base::Pickle hash_state; - if (!secure_hash_->Serialize(&hash_state)) - return std::string(); + if (bytes_so_far_ == 0) + return DOWNLOAD_INTERRUPT_REASON_NONE; - return std::string(reinterpret_cast(hash_state.data()), - hash_state.size()); -} + if (file_.Seek(base::File::FROM_BEGIN, 0) != 0) + return LogSystemError("Seek partial file", + logging::GetLastSystemErrorCode()); + + const size_t kMinBufferSize = secure_hash_->GetHashLength(); + const size_t kMaxBufferSize = 1024 * 512; + + // The size of the buffer is: + // - at least kMinBufferSize so that we can use it to hold the hash as well. + // - at most kMaxBufferSize so that there's a reasonable bound. + // - not larger than |bytes_so_far_| unless bytes_so_far_ is less than the + // hash size. + std::vector buffer(std::max( + kMinBufferSize, std::min(kMaxBufferSize, bytes_so_far_))); + + int64_t current_position = 0; + while (current_position < bytes_so_far_) { + int length = file_.ReadAtCurrentPos(&buffer.front(), buffer.size()); + if (length == -1) { + return LogInterruptReason("Reading partial file", + logging::GetLastSystemErrorCode(), + DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); + } -// static -bool BaseFile::IsEmptyHash(const std::string& hash) { - return (hash.size() == crypto::kSHA256Length && - 0 == memcmp(hash.data(), kEmptySha256Hash, crypto::kSHA256Length)); -} + if (length == 0) + break; -std::string BaseFile::DebugString() const { - return base::StringPrintf("{ source_url_ = \"%s\"" - " full_path_ = \"%" PRFilePath "\"" - " bytes_so_far_ = %" PRId64 - " detached_ = %c }", - source_url_.spec().c_str(), - full_path_.value().c_str(), - bytes_so_far_, - detached_ ? 'T' : 'F'); + secure_hash_->Update(&buffer.front(), length); + current_position += length; + } + + if (current_position != bytes_so_far_) { + return LogInterruptReason( + "Verifying prefix hash", 0, DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); + } + + if (!hash_to_expect.empty()) { + DCHECK_EQ(secure_hash_->GetHashLength(), hash_to_expect.size()); + DCHECK(buffer.size() >= secure_hash_->GetHashLength()); + scoped_ptr partial_hash(secure_hash_->Clone()); + partial_hash->Finish(&buffer.front(), buffer.size()); + + if (memcmp(&buffer.front(), + hash_to_expect.c_str(), + partial_hash->GetHashLength())) { + return LogInterruptReason("Verifying prefix hash", + 0, + DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH); + } + } + + return DOWNLOAD_INTERRUPT_REASON_NONE; } -DownloadInterruptReason BaseFile::Open() { +DownloadInterruptReason BaseFile::Open(const std::string& hash_so_far) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(!detached_); DCHECK(!full_path_.empty()); - bound_net_log_.BeginEvent( - net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, - base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); - // Create a new file if it is not provided. if (!file_.IsValid()) { - file_.Initialize( - full_path_, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE); + file_.Initialize(full_path_, + base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE | + base::File::FLAG_READ); if (!file_.IsValid()) { - return LogNetError("Open", + return LogNetError("Open/Initialize File", net::FileErrorToNetError(file_.error_details())); } } - // We may be re-opening the file after rename. Always make sure we're - // writing at the end of the file. + bound_net_log_.BeginEvent( + net::NetLog::TYPE_DOWNLOAD_FILE_OPENED, + base::Bind(&FileOpenedNetLogCallback, &full_path_, bytes_so_far_)); + + if (!secure_hash_) { + DownloadInterruptReason reason = CalculatePartialHash(hash_so_far); + if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { + ClearFile(); + return reason; + } + } + int64_t file_size = file_.Seek(base::File::FROM_END, 0); if (file_size < 0) { logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); ClearFile(); - return LogSystemError("Seek", error); + return LogSystemError("Seeking to end", error); } else if (file_size > bytes_so_far_) { // The file is larger than we expected. // This is OK, as long as we don't use the extra. @@ -292,7 +308,7 @@ DownloadInterruptReason BaseFile::Open() { file_.Seek(base::File::FROM_BEGIN, bytes_so_far_) != bytes_so_far_) { logging::SystemErrorCode error = logging::GetLastSystemErrorCode(); ClearFile(); - return LogSystemError("Truncate", error); + return LogSystemError("Truncating to last known offset", error); } } else if (file_size < bytes_so_far_) { // The file is shorter than we expected. Our hashes won't be valid. @@ -347,6 +363,9 @@ DownloadInterruptReason BaseFile::LogInterruptReason( const char* operation, int os_error, DownloadInterruptReason reason) { + DVLOG(1) << __FUNCTION__ << "() operation:" << operation + << " os_error:" << os_error + << " reason:" << DownloadInterruptReasonToString(reason); bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_FILE_ERROR, base::Bind(&FileInterruptedNetLogCallback, operation, os_error, reason)); diff --git a/chromium/content/browser/download/base_file.h b/chromium/content/browser/download/base_file.h index cc87ae6ce67..139d43b9bb5 100644 --- a/chromium/content/browser/download/base_file.h +++ b/chromium/content/browser/download/base_file.h @@ -20,40 +20,72 @@ #include "base/time/time.h" #include "content/common/content_export.h" #include "content/public/browser/download_interrupt_reasons.h" -#include "crypto/sha2.h" +#include "crypto/secure_hash.h" #include "net/base/net_errors.h" #include "net/log/net_log.h" #include "url/gurl.h" -namespace crypto { -class SecureHash; -} - namespace content { // File being downloaded and saved to disk. This is a base class -// for DownloadFile and SaveFile, which keep more state information. +// for DownloadFile and SaveFile, which keep more state information. BaseFile +// considers itself the owner of the physical file and will delete it when the +// BaseFile object is destroyed unless the ownership is revoked via a call to +// Detach(). class CONTENT_EXPORT BaseFile { public: // May be constructed on any thread. All other routines (including // destruction) must occur on the FILE thread. - BaseFile(const base::FilePath& full_path, - const GURL& source_url, - const GURL& referrer_url, - int64_t received_bytes, - bool calculate_hash, - const std::string& hash_state, - base::File file, - const net::BoundNetLog& bound_net_log); - virtual ~BaseFile(); + BaseFile(const net::BoundNetLog& bound_net_log); + ~BaseFile(); // Returns DOWNLOAD_INTERRUPT_REASON_NONE on success, or a - // DownloadInterruptReason on failure. |default_directory| specifies the - // directory to create the temporary file in if |full_path()| is empty. If - // |default_directory| and |full_path()| are empty, then a temporary file will - // be created in the default download location as determined by - // ContentBrowserClient. - DownloadInterruptReason Initialize(const base::FilePath& default_directory); + // DownloadInterruptReason on failure. Upon success, the file at |full_path()| + // is assumed to be owned by the BaseFile. It will be deleted when the + // BaseFile object is destroyed unless Detach() is called before destroying + // the BaseFile instance. + // + // |full_path|: Full path to the download file. Can be empty, in which case + // the rules described in |default_directory| will be used to generate a + // temporary filename. + // + // |default_directory|: specifies the directory to create the temporary file + // in if |full_path| is empty. If |default_directory| and |full_path| are + // empty, then a temporary file will be created in the default download + // location as determined by ContentBrowserClient. + // + // |file|: The base::File handle to use. If specified, BaseFile will not open + // a file and will use this handle. The file should be opened for both + // read and write. Only makes sense if |full_path| is non-empty since it + // implies that the caller already knows the path to the file. There's no + // perfect way to come up with a canonical path for a file. So BaseFile + // will not attempt to determine the |full_path|. + // + // |bytes_so_far|: If a file is provided (via |full_path| or |file|), then + // this argument specifies the size of the file to expect. It is legal for + // the file to be larger, in which case the file will be truncated down to + // this size. However, if the file is shorter, then the operation will + // fail with DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT. + // + // |hash_so_far|: If |bytes_so_far| is non-zero, this specifies the SHA-256 + // hash of the first |bytes_so_far| bytes of the target file. If + // specified, BaseFile will read the first |bytes_so_far| of the target + // file in order to calculate the hash and verify that the file matches. + // If there's a mismatch, then the operation fails with + // DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH. Not used if |hash_state| + // is also specified. + // + // |hash_state|: The partial hash object to use. Only meaningful if there's a + // preexisting target file and it is non-empty (i.e. bytes_so_far is + // non-zero). If specified, BaseFile will assume that the bytes up to + // |bytes_so_far| has been accurately hashed into |hash_state| and will + // ignore |hash_so_far|. + DownloadInterruptReason Initialize(const base::FilePath& full_path, + const base::FilePath& default_directory, + base::File file, + int64_t bytes_so_far, + const std::string& hash_so_far, + scoped_ptr hash_state); // Write a new chunk of data to the file. Returns a DownloadInterruptReason // indicating the result of the operation. @@ -63,33 +95,39 @@ class CONTENT_EXPORT BaseFile { // result of the operation. A return code of NONE indicates that the rename // was successful. After a failure, the full_path() and in_progress() can be // used to determine the last known filename and whether the file is available - // for writing or retrying the rename. - virtual DownloadInterruptReason Rename(const base::FilePath& full_path); - - // Detach the file so it is not deleted on destruction. - virtual void Detach(); - - // Abort the download and automatically close the file. + // for writing or retrying the rename. Call Finish() to obtain the last known + // hash state. + DownloadInterruptReason Rename(const base::FilePath& full_path); + + // Mark the file as detached. Up until this method is called, BaseFile assumes + // ownership of the file and hence will delete the file if the BaseFile object + // is destroyed. Calling Detach() causes BaseFile to assume that it no longer + // owns the file. Detach() can be called at any time. Close() must still be + // called to close the file if it is open. + void Detach(); + + // Abort the download and automatically close and delete the file. void Cancel(); // Indicate that the download has finished. No new data will be received. - void Finish(); - - // Indicate that the download is being aborted due to an error. This is - // identical to Finish() with the exception that the hash state will not be - // finalized. - void FinishWithError(); - - // Set the client guid which will be used to identify the app to the - // system AV scanning function. Should be called before - // AnnotateWithSourceInformation() to take effect. - void SetClientGuid(const std::string& guid); + // Returns the SecureHash object representing the state of the hash function + // at the end of the operation. + scoped_ptr Finish(); // Informs the OS that this file came from the internet. Returns a // DownloadInterruptReason indicating the result of the operation. - // Note: SetClientGuid() should be called before this function on - // Windows to ensure the correct app client ID is available. - DownloadInterruptReason AnnotateWithSourceInformation(); + // + // |client_guid|: The client GUID which will be used to identify the caller to + // the system AV scanning function. + // + // |source_url| / |referrer_url|: Source and referrer for the network request + // that originated this download. Will be used to annotate source + // information and also to determine the relative danger level of the + // file. + DownloadInterruptReason AnnotateWithSourceInformation( + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url); // Returns the last known path to the download file. Can be empty if there's // no file. @@ -102,26 +140,27 @@ class CONTENT_EXPORT BaseFile { // Returns the number of bytes in the file pointed to by full_path(). int64_t bytes_so_far() const { return bytes_so_far_; } - // Fills |hash| with the hash digest for the file. - // Returns true if digest is successfully calculated. - virtual bool GetHash(std::string* hash); - - // Returns the current (intermediate) state of the hash as a byte string. - virtual std::string GetHashState(); - - // Returns true if the given hash is considered empty. An empty hash is - // a string of size crypto::kSHA256Length that contains only zeros (initial - // value for the hash). - static bool IsEmptyHash(const std::string& hash); - - virtual std::string DebugString() const; + std::string DebugString() const; private: friend class BaseFileTest; FRIEND_TEST_ALL_PREFIXES(BaseFileTest, IsEmptyHash); - // Creates and opens the file_ if it is NULL. - DownloadInterruptReason Open(); + // Creates and opens the file_ if it is invalid. + // + // If |hash_so_far| is not empty, then it must match the SHA-256 hash of the + // first |bytes_so_far_| bytes of |file_|. If there's a hash mismatch, Open() + // fails with DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH. + // + // If the opened file is shorter than |bytes_so_far_| bytes, then Open() fails + // with DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT. If the opened file is + // longer, then the file is truncated to |bytes_so_far_|. + // + // Open() can fail for other reasons as well. In that case, it returns a + // relevant interrupt reason. Unless Open() return + // DOWNLOAD_INTERRUPT_REASON_NONE, it should be assumed that |file_| is not + // valid. + DownloadInterruptReason Open(const std::string& hash_so_far); // Closes and resets file_. void Close(); @@ -138,6 +177,17 @@ class CONTENT_EXPORT BaseFile { // Split out from CurrentSpeed to enable testing. int64_t CurrentSpeedAtTime(base::TimeTicks current_time) const; + // Verifies that: + // * Size of the file represented by |file_| is at least |bytes_so_far_|. + // + // * If |hash_to_expect| is not empty, then the result of hashing the first + // |bytes_so_far_| bytes of |file_| matches |hash_to_expect|. + // + // If the result is REASON_NONE, then on return |secure_hash_| is valid and + // is ready to hash bytes from offset |bytes_so_far_| + 1. + DownloadInterruptReason CalculatePartialHash( + const std::string& hash_to_expect); + // Log a TYPE_DOWNLOAD_FILE_ERROR NetLog event with |error| and passes error // on through, converting to a |DownloadInterruptReason|. DownloadInterruptReason LogNetError(const char* operation, net::Error error); @@ -153,40 +203,24 @@ class CONTENT_EXPORT BaseFile { const char* operation, int os_error, DownloadInterruptReason reason); - static const unsigned char kEmptySha256Hash[crypto::kSHA256Length]; - // Full path to the file including the file name. base::FilePath full_path_; - // Source URL for the file being downloaded. - GURL source_url_; - - // The URL where the download was initiated. - GURL referrer_url_; - - std::string client_guid_; - // OS file for writing base::File file_; // Amount of data received up so far, in bytes. - int64_t bytes_so_far_; + int64_t bytes_so_far_ = 0; - // Start time for calculating speed. - base::TimeTicks start_tick_; - - // Indicates if hash should be calculated for the file. - bool calculate_hash_; - - // Used to calculate hash for the file when calculate_hash_ - // is set. + // Used to calculate hash for the file when calculate_hash_ is set. scoped_ptr secure_hash_; - unsigned char sha256_hash_[crypto::kSHA256Length]; + // Start time for calculating speed. + base::TimeTicks start_tick_; // Indicates that this class no longer owns the associated file, and so // won't delete it on destruction. - bool detached_; + bool detached_ = false; net::BoundNetLog bound_net_log_; diff --git a/chromium/content/browser/download/base_file_linux.cc b/chromium/content/browser/download/base_file_linux.cc index 721cd602694..6c62be32302 100644 --- a/chromium/content/browser/download/base_file_linux.cc +++ b/chromium/content/browser/download/base_file_linux.cc @@ -9,11 +9,14 @@ namespace content { -DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { +DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(!detached_); - AddOriginMetadataToFile(full_path_, source_url_, referrer_url_); + AddOriginMetadataToFile(full_path_, source_url, referrer_url); return DOWNLOAD_INTERRUPT_REASON_NONE; } diff --git a/chromium/content/browser/download/base_file_mac.cc b/chromium/content/browser/download/base_file_mac.cc index f3edf12d094..4ece61b90d7 100644 --- a/chromium/content/browser/download/base_file_mac.cc +++ b/chromium/content/browser/download/base_file_mac.cc @@ -9,12 +9,15 @@ namespace content { -DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { +DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(!detached_); - AddQuarantineMetadataToFile(full_path_, source_url_, referrer_url_); - AddOriginMetadataToFile(full_path_, source_url_, referrer_url_); + AddQuarantineMetadataToFile(full_path_, source_url, referrer_url); + AddOriginMetadataToFile(full_path_, source_url, referrer_url); return DOWNLOAD_INTERRUPT_REASON_NONE; } diff --git a/chromium/content/browser/download/base_file_unittest.cc b/chromium/content/browser/download/base_file_unittest.cc index a6ce347c189..8f2412b17d9 100644 --- a/chromium/content/browser/download/base_file_unittest.cc +++ b/chromium/content/browser/download/base_file_unittest.cc @@ -32,12 +32,24 @@ const char kTestData3[] = "Final line."; const char kTestData4[] = "supercalifragilisticexpialidocious"; const int kTestDataLength1 = arraysize(kTestData1) - 1; const int kTestDataLength2 = arraysize(kTestData2) - 1; -const int kTestDataLength3 = arraysize(kTestData3) - 1; const int kTestDataLength4 = arraysize(kTestData4) - 1; const int kElapsedTimeSeconds = 5; const base::TimeDelta kElapsedTimeDelta = base::TimeDelta::FromSeconds( kElapsedTimeSeconds); +// SHA-256 hash of kTestData1 (excluding terminating NUL). +const uint8_t kHashOfTestData1[] = { + 0x0b, 0x2d, 0x3f, 0x3f, 0x79, 0x43, 0xad, 0x64, 0xb8, 0x60, 0xdf, + 0x94, 0xd0, 0x5c, 0xb5, 0x6a, 0x8a, 0x97, 0xc6, 0xec, 0x57, 0x68, + 0xb5, 0xb7, 0x0b, 0x93, 0x0c, 0x5a, 0xa7, 0xfa, 0x9a, 0xde}; + +// SHA-256 hash of kTestData1 ++ kTestData2 ++ kTestData3 (excluding terminating +// NUL). +const uint8_t kHashOfTestData1To3[] = { + 0xcb, 0xf6, 0x8b, 0xf1, 0x0f, 0x80, 0x03, 0xdb, 0x86, 0xb3, 0x13, + 0x43, 0xaf, 0xac, 0x8c, 0x71, 0x75, 0xbd, 0x03, 0xfb, 0x5f, 0xc9, + 0x05, 0x65, 0x0f, 0x8c, 0x80, 0xaf, 0x08, 0x74, 0x43, 0xa8}; + } // namespace class BaseFileTest : public testing::Test { @@ -52,16 +64,8 @@ class BaseFileTest : public testing::Test { } void SetUp() override { - ResetHash(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - base_file_.reset(new BaseFile(base::FilePath(), - GURL(), - GURL(), - 0, - false, - std::string(), - base::File(), - net::BoundNetLog())); + base_file_.reset(new BaseFile(net::BoundNetLog())); } void TearDown() override { @@ -87,36 +91,14 @@ class BaseFileTest : public testing::Test { EXPECT_EQ(expect_file_survives_, base::PathExists(full_path)); } - void ResetHash() { - secure_hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); - memcpy(sha256_hash_, kEmptySha256Hash, crypto::kSHA256Length); - } - - void UpdateHash(const char* data, size_t length) { - secure_hash_->Update(data, length); - } - - std::string GetFinalHash() { - std::string hash; - secure_hash_->Finish(sha256_hash_, crypto::kSHA256Length); - hash.assign(reinterpret_cast(sha256_hash_), - sizeof(sha256_hash_)); - return hash; - } - - void MakeFileWithHash() { - base_file_.reset(new BaseFile(base::FilePath(), - GURL(), - GURL(), - 0, - true, - std::string(), - base::File(), - net::BoundNetLog())); - } - bool InitializeFile() { - DownloadInterruptReason result = base_file_->Initialize(temp_dir_.path()); + DownloadInterruptReason result = + base_file_->Initialize(base::FilePath(), + temp_dir_.path(), + base::File(), + 0, + std::string(), + scoped_ptr()); EXPECT_EQ(expected_error_, result); return result == DOWNLOAD_INTERRUPT_REASON_NONE; } @@ -145,17 +127,15 @@ class BaseFileTest : public testing::Test { // Create a file. Returns the complete file path. base::FilePath CreateTestFile() { base::FilePath file_name; - BaseFile file(base::FilePath(), - GURL(), - GURL(), - 0, - false, - std::string(), - base::File(), - net::BoundNetLog()); + BaseFile file((net::BoundNetLog())); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, - file.Initialize(temp_dir_.path())); + file.Initialize(base::FilePath(), + temp_dir_.path(), + base::File(), + 0, + std::string(), + scoped_ptr())); file_name = file.full_path(); EXPECT_NE(base::FilePath::StringType(), file_name.value()); @@ -171,16 +151,14 @@ class BaseFileTest : public testing::Test { // Create a file with the specified file name. void CreateFileWithName(const base::FilePath& file_name) { EXPECT_NE(base::FilePath::StringType(), file_name.value()); - BaseFile duplicate_file(file_name, - GURL(), - GURL(), - 0, - false, - std::string(), - base::File(), - net::BoundNetLog()); + BaseFile duplicate_file((net::BoundNetLog())); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, - duplicate_file.Initialize(temp_dir_.path())); + duplicate_file.Initialize(file_name, + temp_dir_.path(), + base::File(), + 0, + std::string(), + scoped_ptr())); // Write something into it. duplicate_file.AppendDataToFile(kTestData4, kTestDataLength4); // Detach the file so it isn't deleted on destruction of |duplicate_file|. @@ -207,6 +185,15 @@ class BaseFileTest : public testing::Test { << "Interrupt reason = " << err; } + template + static void ExpectHashValue(const uint8_t (&expected_hash)[SZ], + scoped_ptr hash_state) { + std::vector hash_value(hash_state->GetHashLength()); + hash_state->Finish(&hash_value.front(), hash_value.size()); + ASSERT_EQ(SZ, hash_value.size()); + EXPECT_EQ(0, memcmp(expected_hash, &hash_value.front(), hash_value.size())); + } + protected: // BaseClass instance we are testing. scoped_ptr base_file_; @@ -220,11 +207,6 @@ class BaseFileTest : public testing::Test { // Expect the file to be in progress. bool expect_in_progress_; - // Hash calculator. - scoped_ptr secure_hash_; - - unsigned char sha256_hash_[crypto::kSHA256Length]; - private: // Keep track of what data should be saved to the disk file. std::string expected_data_; @@ -266,24 +248,9 @@ TEST_F(BaseFileTest, WriteAndDetach) { // Write data to the file and detach it, and calculate its sha256 hash. TEST_F(BaseFileTest, WriteWithHashAndDetach) { - // Calculate the final hash. - ResetHash(); - UpdateHash(kTestData1, kTestDataLength1); - std::string expected_hash = GetFinalHash(); - std::string expected_hash_hex = - base::HexEncode(expected_hash.data(), expected_hash.size()); - - MakeFileWithHash(); ASSERT_TRUE(InitializeFile()); ASSERT_TRUE(AppendDataToFile(kTestData1)); - base_file_->Finish(); - - std::string hash; - base_file_->GetHash(&hash); - EXPECT_EQ("0B2D3F3F7943AD64B860DF94D05CB56A8A97C6EC5768B5B70B930C5AA7FA9ADE", - expected_hash_hex); - EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); - + ExpectHashValue(kHashOfTestData1, base_file_->Finish()); base_file_->Detach(); expect_file_survives_ = true; } @@ -303,7 +270,7 @@ TEST_F(BaseFileTest, WriteThenRenameAndDetach) { EXPECT_FALSE(base::PathExists(initial_path)); EXPECT_TRUE(base::PathExists(new_path)); - base_file_->Finish(); + ExpectHashValue(kHashOfTestData1, base_file_->Finish()); base_file_->Detach(); expect_file_survives_ = true; } @@ -312,7 +279,7 @@ TEST_F(BaseFileTest, WriteThenRenameAndDetach) { TEST_F(BaseFileTest, SingleWrite) { ASSERT_TRUE(InitializeFile()); ASSERT_TRUE(AppendDataToFile(kTestData1)); - base_file_->Finish(); + ExpectHashValue(kHashOfTestData1, base_file_->Finish()); } // Write data to the file multiple times. @@ -321,82 +288,18 @@ TEST_F(BaseFileTest, MultipleWrites) { ASSERT_TRUE(AppendDataToFile(kTestData1)); ASSERT_TRUE(AppendDataToFile(kTestData2)); ASSERT_TRUE(AppendDataToFile(kTestData3)); - std::string hash; - EXPECT_FALSE(base_file_->GetHash(&hash)); - base_file_->Finish(); -} - -// Write data to the file once and calculate its sha256 hash. -TEST_F(BaseFileTest, SingleWriteWithHash) { - // Calculate the final hash. - ResetHash(); - UpdateHash(kTestData1, kTestDataLength1); - std::string expected_hash = GetFinalHash(); - std::string expected_hash_hex = - base::HexEncode(expected_hash.data(), expected_hash.size()); - - MakeFileWithHash(); - ASSERT_TRUE(InitializeFile()); - // Can get partial hash states before Finish() is called. - EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str()); - ASSERT_TRUE(AppendDataToFile(kTestData1)); - EXPECT_STRNE(std::string().c_str(), base_file_->GetHashState().c_str()); - base_file_->Finish(); - - std::string hash; - base_file_->GetHash(&hash); - EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); -} - -// Write data to the file multiple times and calculate its sha256 hash. -TEST_F(BaseFileTest, MultipleWritesWithHash) { - // Calculate the final hash. - ResetHash(); - UpdateHash(kTestData1, kTestDataLength1); - UpdateHash(kTestData2, kTestDataLength2); - UpdateHash(kTestData3, kTestDataLength3); - std::string expected_hash = GetFinalHash(); - std::string expected_hash_hex = - base::HexEncode(expected_hash.data(), expected_hash.size()); - - std::string hash; - MakeFileWithHash(); - ASSERT_TRUE(InitializeFile()); - ASSERT_TRUE(AppendDataToFile(kTestData1)); - ASSERT_TRUE(AppendDataToFile(kTestData2)); - ASSERT_TRUE(AppendDataToFile(kTestData3)); - // No hash before Finish() is called. - EXPECT_FALSE(base_file_->GetHash(&hash)); - base_file_->Finish(); - - EXPECT_TRUE(base_file_->GetHash(&hash)); - EXPECT_EQ("CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8", - expected_hash_hex); - EXPECT_EQ(expected_hash_hex, base::HexEncode(hash.data(), hash.size())); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); } // Write data to the file multiple times, interrupt it, and continue using // another file. Calculate the resulting combined sha256 hash. TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) { - // Calculate the final hash. - ResetHash(); - UpdateHash(kTestData1, kTestDataLength1); - UpdateHash(kTestData2, kTestDataLength2); - UpdateHash(kTestData3, kTestDataLength3); - std::string expected_hash = GetFinalHash(); - std::string expected_hash_hex = - base::HexEncode(expected_hash.data(), expected_hash.size()); - - MakeFileWithHash(); ASSERT_TRUE(InitializeFile()); // Write some data ASSERT_TRUE(AppendDataToFile(kTestData1)); ASSERT_TRUE(AppendDataToFile(kTestData2)); // Get the hash state and file name. - std::string hash_state; - hash_state = base_file_->GetHashState(); - // Finish the file. - base_file_->Finish(); + scoped_ptr hash_state = base_file_->Finish(); base::FilePath new_file_path(temp_dir_.path().Append( base::FilePath(FILE_PATH_LITERAL("second_file")))); @@ -404,26 +307,18 @@ TEST_F(BaseFileTest, MultipleWritesInterruptedWithHash) { ASSERT_TRUE(base::CopyFile(base_file_->full_path(), new_file_path)); // Create another file - BaseFile second_file(new_file_path, - GURL(), - GURL(), - base_file_->bytes_so_far(), - true, - hash_state, - base::File(), - net::BoundNetLog()); + BaseFile second_file((net::BoundNetLog())); ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, - second_file.Initialize(base::FilePath())); + second_file.Initialize(new_file_path, + base::FilePath(), + base::File(), + base_file_->bytes_so_far(), + std::string(), + std::move(hash_state))); std::string data(kTestData3); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, second_file.AppendDataToFile(data.data(), data.size())); - second_file.Finish(); - - std::string hash; - EXPECT_TRUE(second_file.GetHash(&hash)); - // This will fail until getting the hash state is supported in SecureHash. - EXPECT_STREQ(expected_hash_hex.c_str(), - base::HexEncode(hash.data(), hash.size()).c_str()); + ExpectHashValue(kHashOfTestData1To3, second_file.Finish()); } // Rename the file after all writes to it. @@ -442,7 +337,7 @@ TEST_F(BaseFileTest, WriteThenRename) { EXPECT_FALSE(base::PathExists(initial_path)); EXPECT_TRUE(base::PathExists(new_path)); - base_file_->Finish(); + ExpectHashValue(kHashOfTestData1, base_file_->Finish()); } // Rename the file while the download is still in progress. @@ -462,8 +357,9 @@ TEST_F(BaseFileTest, RenameWhileInProgress) { EXPECT_TRUE(base::PathExists(new_path)); ASSERT_TRUE(AppendDataToFile(kTestData2)); + ASSERT_TRUE(AppendDataToFile(kTestData3)); - base_file_->Finish(); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); } // Test that a failed rename reports the correct error. @@ -525,15 +421,7 @@ TEST_F(BaseFileTest, RenameWithErrorInProgress) { ASSERT_EQ(new_path.value(), base_file_->full_path().value()); ASSERT_TRUE(AppendDataToFile(kTestData3)); - base_file_->Finish(); - - // The contents of the file should be intact. - std::string file_contents; - std::string expected_contents(kTestData1); - expected_contents += kTestData2; - expected_contents += kTestData3; - ASSERT_TRUE(base::ReadFileToString(new_path, &file_contents)); - EXPECT_EQ(expected_contents, file_contents); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); } // Test that a failed write reports an error. @@ -544,9 +432,14 @@ TEST_F(BaseFileTest, WriteWithError) { // Pass a file handle which was opened without the WRITE flag. // This should result in an error when writing. base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ); - base_file_.reset(new BaseFile(path, GURL(), GURL(), 0, false, std::string(), - std::move(file), net::BoundNetLog())); - ASSERT_TRUE(InitializeFile()); + base_file_.reset(new BaseFile(net::BoundNetLog())); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(path, + base::FilePath(), + std::move(file), + 0, + std::string(), + scoped_ptr())); #if defined(OS_WIN) set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED); #elif defined (OS_POSIX) @@ -580,20 +473,17 @@ TEST_F(BaseFileTest, DuplicateBaseFile) { TEST_F(BaseFileTest, AppendToBaseFile) { // Create a new file. base::FilePath existing_file_name = CreateTestFile(); - set_expected_data(kTestData4); // Use the file we've just created. - base_file_.reset(new BaseFile(existing_file_name, - GURL(), - GURL(), - kTestDataLength4, - false, - std::string(), - base::File(), - net::BoundNetLog())); - - ASSERT_TRUE(InitializeFile()); + base_file_.reset(new BaseFile(net::BoundNetLog())); + ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(existing_file_name, + base::FilePath(), + base::File(), + kTestDataLength4, + std::string(), + scoped_ptr())); const base::FilePath file_name = base_file_->full_path(); EXPECT_NE(base::FilePath::StringType(), file_name.value()); @@ -618,18 +508,16 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) { EXPECT_TRUE(base::MakeFileUnwritable(readonly_file_name)); // Try to overwrite it. - base_file_.reset(new BaseFile(readonly_file_name, - GURL(), - GURL(), - 0, - false, - std::string(), - base::File(), - net::BoundNetLog())); + base_file_.reset(new BaseFile(net::BoundNetLog())); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, + base_file_->Initialize(readonly_file_name, + base::FilePath(), + base::File(), + 0, + std::string(), + scoped_ptr())); expect_in_progress_ = false; - set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED); - EXPECT_FALSE(InitializeFile()); const base::FilePath file_name = base_file_->full_path(); EXPECT_NE(base::FilePath::StringType(), file_name.value()); @@ -643,16 +531,175 @@ TEST_F(BaseFileTest, ReadonlyBaseFile) { expect_file_survives_ = true; } -TEST_F(BaseFileTest, IsEmptyHash) { - std::string empty(crypto::kSHA256Length, '\x00'); - EXPECT_TRUE(BaseFile::IsEmptyHash(empty)); - std::string not_empty(crypto::kSHA256Length, '\x01'); - EXPECT_FALSE(BaseFile::IsEmptyHash(not_empty)); - EXPECT_FALSE(BaseFile::IsEmptyHash(std::string())); +// Open an existing file and continue writing to it. The hash of the partial +// file is known and matches the existing contents. +TEST_F(BaseFileTest, ExistingBaseFileKnownHash) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + ASSERT_TRUE(base::WriteFile(file_path, kTestData1, kTestDataLength1)); + + std::string hash_so_far(std::begin(kHashOfTestData1), + std::end(kHashOfTestData1)); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength1, + hash_so_far, + scoped_ptr())); + set_expected_data(kTestData1); + ASSERT_TRUE(AppendDataToFile(kTestData2)); + ASSERT_TRUE(AppendDataToFile(kTestData3)); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); +} + +// Open an existing file and continue writing to it. The hash of the partial +// file is unknown. +TEST_F(BaseFileTest, ExistingBaseFileUnknownHash) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + ASSERT_TRUE(base::WriteFile(file_path, kTestData1, kTestDataLength1)); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength1, + std::string(), + scoped_ptr())); + set_expected_data(kTestData1); + ASSERT_TRUE(AppendDataToFile(kTestData2)); + ASSERT_TRUE(AppendDataToFile(kTestData3)); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); +} + +// Open an existing file. The contentsof the file doesn't match the known hash. +TEST_F(BaseFileTest, ExistingBaseFileIncorrectHash) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + ASSERT_TRUE(base::WriteFile(file_path, kTestData2, kTestDataLength2)); + + std::string hash_so_far(std::begin(kHashOfTestData1), + std::end(kHashOfTestData1)); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength2, + hash_so_far, + scoped_ptr())); + set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH); +} - std::string also_not_empty = empty; - also_not_empty[crypto::kSHA256Length - 1] = '\x01'; - EXPECT_FALSE(BaseFile::IsEmptyHash(also_not_empty)); +// Open a large existing file with a known hash and continue writing to it. +TEST_F(BaseFileTest, ExistingBaseFileLargeSizeKnownHash) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + std::string big_buffer(1024 * 200, 'a'); + ASSERT_TRUE(base::WriteFile(file_path, big_buffer.data(), big_buffer.size())); + + // Hash of partial file (1024*200 * 'a') + const uint8_t kExpectedPartialHash[] = { + 0x4b, 0x4f, 0x0f, 0x46, 0xac, 0x02, 0xd1, 0x77, 0xde, 0xa0, 0xab, + 0x36, 0xa6, 0x6a, 0x65, 0x78, 0x40, 0xe2, 0xfb, 0x98, 0xb2, 0x0b, + 0xb2, 0x7a, 0x68, 0x8d, 0xb4, 0xd8, 0xea, 0x9c, 0xd2, 0x2c}; + + // Hash of entire file (1024*400 * 'a') + const uint8_t kExpectedFullHash[] = { + 0x0c, 0xe9, 0xf6, 0x78, 0x6b, 0x0f, 0x58, 0x49, 0x36, 0xe8, 0x83, + 0xc5, 0x09, 0x16, 0xbc, 0x5e, 0x2d, 0x07, 0x95, 0xb9, 0x42, 0x20, + 0x41, 0x7c, 0xb3, 0x38, 0xd3, 0xf4, 0xe0, 0x78, 0x89, 0x46}; + + ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + big_buffer.size(), + std::string(std::begin(kExpectedPartialHash), + std::end(kExpectedPartialHash)), + scoped_ptr())); + set_expected_data(big_buffer); // Contents of the file on Open. + ASSERT_TRUE(AppendDataToFile(big_buffer)); + ExpectHashValue(kExpectedFullHash, base_file_->Finish()); +} + +// Open a large existing file. The contents doesn't match the known hash. +TEST_F(BaseFileTest, ExistingBaseFileLargeSizeIncorrectHash) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + std::string big_buffer(1024 * 200, 'a'); + ASSERT_TRUE(base::WriteFile(file_path, big_buffer.data(), big_buffer.size())); + + // Incorrect hash of partial file (1024*200 * 'a') + const uint8_t kExpectedPartialHash[] = { + 0xc2, 0xa9, 0x08, 0xd9, 0x8f, 0x5d, 0xf9, 0x87, 0xad, 0xe4, 0x1b, + 0x5f, 0xce, 0x21, 0x30, 0x67, 0xef, 0x6c, 0xc2, 0x1e, 0xf2, 0x24, + 0x02, 0x12, 0xa4, 0x1e, 0x54, 0xb5, 0xe7, 0xc2, 0x8a, 0xe5}; + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + big_buffer.size(), + std::string(std::begin(kExpectedPartialHash), + std::end(kExpectedPartialHash)), + scoped_ptr())); + set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH); +} + +// Open an existing file. The size of the file is too short. +TEST_F(BaseFileTest, ExistingBaseFileTooShort) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + ASSERT_TRUE(base::WriteFile(file_path, kTestData1, kTestDataLength1)); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength1 + 1, + std::string(), + scoped_ptr())); + set_expected_error(DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT); +} + +// Open an existing file. The size is larger than expected. +TEST_F(BaseFileTest, ExistingBaseFileKnownHashTooLong) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + std::string contents; + contents.append(kTestData1); + contents.append("Something extra"); + ASSERT_TRUE(base::WriteFile(file_path, contents.data(), contents.size())); + + std::string hash_so_far(std::begin(kHashOfTestData1), + std::end(kHashOfTestData1)); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength1, + hash_so_far, + scoped_ptr())); + set_expected_data(kTestData1); // Our starting position. + ASSERT_TRUE(AppendDataToFile(kTestData2)); + ASSERT_TRUE(AppendDataToFile(kTestData3)); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); +} + +// Open an existing file. The size is large than expected and the hash is +// unknown. +TEST_F(BaseFileTest, ExistingBaseFileUnknownHashTooLong) { + base::FilePath file_path = temp_dir_.path().AppendASCII("existing"); + std::string contents; + contents.append(kTestData1); + contents.append("Something extra"); + ASSERT_TRUE(base::WriteFile(file_path, contents.data(), contents.size())); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, + base_file_->Initialize(file_path, + base::FilePath(), + base::File(), + kTestDataLength1, + std::string(), + scoped_ptr())); + set_expected_data(kTestData1); + ASSERT_TRUE(AppendDataToFile(kTestData2)); + ASSERT_TRUE(AppendDataToFile(kTestData3)); + ExpectHashValue(kHashOfTestData1To3, base_file_->Finish()); } // Test that a temporary file is created in the default download directory. diff --git a/chromium/content/browser/download/base_file_win.cc b/chromium/content/browser/download/base_file_win.cc index bfdf3aa9da6..5fa373a43ec 100644 --- a/chromium/content/browser/download/base_file_win.cc +++ b/chromium/content/browser/download/base_file_win.cc @@ -352,22 +352,25 @@ DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions( return interrupt_reason; } -DownloadInterruptReason BaseFile::AnnotateWithSourceInformation() { +DownloadInterruptReason BaseFile::AnnotateWithSourceInformation( + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(!detached_); bound_net_log_.BeginEvent(net::NetLog::TYPE_DOWNLOAD_FILE_ANNOTATED); DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; - std::string braces_guid = "{" + client_guid_ + "}"; + std::string braces_guid = "{" + client_guid + "}"; GUID guid = GUID_NULL; - if (base::IsValidGUID(client_guid_)) { + if (base::IsValidGUID(client_guid)) { HRESULT hr = CLSIDFromString( base::UTF8ToUTF16(braces_guid).c_str(), &guid); if (FAILED(hr)) guid = GUID_NULL; } - HRESULT hr = AVScanFile(full_path_, source_url_.spec(), guid); + HRESULT hr = AVScanFile(full_path_, source_url.spec(), guid); // If the download file is missing after the call, then treat this as an // interrupted download. diff --git a/chromium/content/browser/download/docs/save-page-as.md b/chromium/content/browser/download/docs/save-page-as.md new file mode 100644 index 00000000000..2661289b375 --- /dev/null +++ b/chromium/content/browser/download/docs/save-page-as.md @@ -0,0 +1,137 @@ +# High-level overview of Save-Page-As code + +This document describes code under `//content/browser/downloads` +restricting the scope only to code handling Save-Page-As functionality +(i.e. leaving out other downloads-related code). +This document focuses on high-level overview and aspects of the code that +span multiple compilation units (hoping that individual compilation units +are described by their code comments or by their code structure). + +## Classes overview + +* SavePackage class + * coordinates overall save-page-as request + * created and owned by `WebContents` + (ref-counted today, but it is unnecessary - see https://crbug.com/596953) + * UI-thread object + +* SaveFileCreateInfo::SaveFileSource enum + * classifies `SaveItem` and `SaveFile` processing into 3 flavours: + * `SAVE_FILE_FROM_NET` (see `SaveFileResourceHandler`) + * `SAVE_FILE_FROM_DOM` (see "Complete HTML" section below) + * `SAVE_FILE_FROM_FILE` (see `SaveFileManager::SaveLocalFile`) + +* SaveItem class + * tracks saving a single file + * created and owned by `SavePackage` + * UI-thread object + +* SaveFileManager class + * coordinates between FILE and UI threads + * Gets requests from `SavePackage` and communicates results back to + `SavePackage` on the UI thread. + * Shephards data (received from the network OR from DOM) into + FILE thread - via `SaveFileManager::UpdateSaveProgress` + * created and owned by `ResourceDispatchedHostImpl` + (ref-counted today, but it is unnecessary - see https://crbug.com/596953) + +* SaveFile class + * tracks saving a single file + * created and owned by `SaveFileManager` + * FILE-thread object + +* SaveFileResourceHandler class + * tracks network downloads + forwards their status into `SaveFileManager` + (onto FILE-thread) + * created by `ResourceDispatcherHostImpl::BeginSaveFile` + * IO-thread object + +* SaveFileCreateInfo POD struct + * short-lived object holding data passed to callbacks handling start of + saving a file. + +* MHTMLGenerationManager class + * singleton that manages progress of jobs responsible for saving individual + MHTML files (represented by `MHTMLGenerationManager::Job`). + + +## Overview of the processing flow + +Save-Page-As flow starts with `WebContents::OnSavePage`. +The flow is different depending on the save format chosen by the user +(each flow is described in a separate section below). + +### Complete HTML + +Very high-level flow of saving a page as "Complete HTML": + +* Step 1: `SavePackage` asks all frames for "savable resources" + and creates `SaveItem` for each of files that need to be saved + +* Step 2: `SavePackage` first processes `SAVE_FILE_FROM_NET` and + `SAVE_FILE_FROM_FILE` `SaveItem`s and asks `SaveFileManager` to save + them. + +* Step 3: `SavePackage` handles remaining `SAVE_FILE_FROM_DOM` `SaveItem`s and + asks each frame to serialize its DOM/HTML (each frame gets from + `SavePackage` a map covering local paths that need to be referenced by + the frame). Responses from frames get forwarded to `SaveFileManager` + to be written to disk. + + +### MHTML + +Very high-level flow of saving a page as MHTML: + +* Step 1: `WebContents::GenerateMHTML` is called by either `SavePackage` (for + Save-Page-As UI) or Extensions (via `chrome.pageCapture` extensions + API) or by an embedder of `WebContents` (since this is public API of + //content). + +* Step 2: `MHTMLGenerationManager` coordinates generation of the MHTML file + by sequentially (one-at-a-time) asking each frame to write its portion + of MHTML to a file handle. Other classes (i.e. `SavePackage` and/or + `SaveFileManager`) are not used at this step at all. + +* Step 3: When done `MHTMLGenerationManager` calls a completion callback + which in case of Save-Page-As will end up in + `SavePackage::OnMHTMLGenerated`. + +Note: MHTML format is by default disabled in Save-Page-As UI on Windows, MacOS +and Linux (it is the default on ChromeOS), but for testing this can be easily +changed using `--save-page-as-mhtml` command line switch. + + +### HTML Only + +Very high-level flow of saving a page as "HTML Only": + +* `SavePackage` creates only a single `SaveItem` (either `SAVE_FILE_FROM_NET` or + `SAVE_FILE_FROM_FILE`) and asks `SaveFileManager` to process it + (as in the Complete HTML individual SaveItem handling above.). + + +## Other relevant code + +Pointers to related code outside of `//content/browser/download`: + +* End-to-end tests: + * `//chrome/browser/downloads/save_page_browsertest.cc` + * `//chrome/test/data/save_page/...` + +* Other tests: + * `//content/browser/downloads/*test*.cc` + * `//content/renderer/dom_serializer_browsertest.cc` - single process... :-/ + +* Elsewhere in `//content`: + * `//content/renderer/savable_resources...` + +* Blink: + * `//third_party/WebKit/public/web/WebFrameSerializer...` + * `//third_party/WebKit/Source/web/WebFrameSerializerImpl...` + (used for Complete HTML today; should use `FrameSerializer` instead in + the long-term - see https://crbug.com/328354). + * `//third_party/WebKit/Source/core/frame/FrameSerializer...` + (used for MHTML today) + * `//third_party/WebKit/Source/platform/mhtml/MHTMLArchive...` + diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc index dc965d41646..19a05a2d6a0 100644 --- a/chromium/content/browser/download/download_browsertest.cc +++ b/chromium/content/browser/download/download_browsertest.cc @@ -29,8 +29,12 @@ #include "content/browser/download/download_item_impl.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_resource_handler.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/download_danger_type.h" #include "content/public/browser/power_save_blocker.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "content/public/browser/resource_throttle.h" #include "content/public/common/content_features.h" #include "content/public/common/webplugininfo.h" #include "content/public/test/browser_test_utils.h" @@ -118,17 +122,13 @@ static DownloadManagerImpl* DownloadManagerForShell(Shell* shell) { class DownloadFileWithDelay : public DownloadFileImpl { public: - DownloadFileWithDelay( - scoped_ptr save_info, - const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr stream, - const net::BoundNetLog& bound_net_log, - scoped_ptr power_save_blocker, - base::WeakPtr observer, - base::WeakPtr owner); + DownloadFileWithDelay(scoped_ptr save_info, + const base::FilePath& default_download_directory, + scoped_ptr stream, + const net::BoundNetLog& bound_net_log, + scoped_ptr power_save_blocker, + base::WeakPtr observer, + base::WeakPtr owner); ~DownloadFileWithDelay() override; @@ -138,6 +138,9 @@ class DownloadFileWithDelay : public DownloadFileImpl { void RenameAndUniquify(const base::FilePath& full_path, const RenameCompletionCallback& callback) override; void RenameAndAnnotate(const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, const RenameCompletionCallback& callback) override; private: @@ -167,9 +170,6 @@ class DownloadFileWithDelayFactory : public DownloadFileFactory { DownloadFile* CreateFile( scoped_ptr save_info, const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) override; @@ -191,9 +191,6 @@ class DownloadFileWithDelayFactory : public DownloadFileFactory { DownloadFileWithDelay::DownloadFileWithDelay( scoped_ptr save_info, const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, scoped_ptr power_save_blocker, @@ -201,9 +198,6 @@ DownloadFileWithDelay::DownloadFileWithDelay( base::WeakPtr owner) : DownloadFileImpl(std::move(save_info), default_download_directory, - url, - referrer_url, - calculate_hash, std::move(stream), bound_net_log, observer), @@ -221,11 +215,19 @@ void DownloadFileWithDelay::RenameAndUniquify( } void DownloadFileWithDelay::RenameAndAnnotate( - const base::FilePath& full_path, const RenameCompletionCallback& callback) { + const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, + const RenameCompletionCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DownloadFileImpl::RenameAndAnnotate( - full_path, base::Bind(DownloadFileWithDelay::RenameCallbackWrapper, - owner_, callback)); + full_path, + client_guid, + source_url, + referrer_url, + base::Bind( + DownloadFileWithDelay::RenameCallbackWrapper, owner_, callback)); } // static @@ -249,19 +251,19 @@ DownloadFileWithDelayFactory::~DownloadFileWithDelayFactory() {} DownloadFile* DownloadFileWithDelayFactory::CreateFile( scoped_ptr save_info, const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) { scoped_ptr psb(PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress")); - return new DownloadFileWithDelay( - std::move(save_info), default_download_directory, url, referrer_url, - calculate_hash, std::move(stream), bound_net_log, std::move(psb), - observer, weak_ptr_factory_.GetWeakPtr()); + return new DownloadFileWithDelay(std::move(save_info), + default_download_directory, + std::move(stream), + bound_net_log, + std::move(psb), + observer, + weak_ptr_factory_.GetWeakPtr()); } void DownloadFileWithDelayFactory::AddRenameCallback(base::Closure callback) { @@ -291,18 +293,12 @@ class CountingDownloadFile : public DownloadFileImpl { public: CountingDownloadFile(scoped_ptr save_info, const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, scoped_ptr power_save_blocker, base::WeakPtr observer) : DownloadFileImpl(std::move(save_info), default_downloads_directory, - url, - referrer_url, - calculate_hash, std::move(stream), bound_net_log, observer) {} @@ -351,19 +347,18 @@ class CountingDownloadFileFactory : public DownloadFileFactory { DownloadFile* CreateFile( scoped_ptr save_info, const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) override { scoped_ptr psb(PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress")); - return new CountingDownloadFile( - std::move(save_info), default_downloads_directory, url, referrer_url, - calculate_hash, std::move(stream), bound_net_log, std::move(psb), - observer); + return new CountingDownloadFile(std::move(save_info), + default_downloads_directory, + std::move(stream), + bound_net_log, + std::move(psb), + observer); } }; @@ -551,6 +546,13 @@ class TestRequestStartHandler { class DownloadContentTest : public ContentBrowserTest { protected: void SetUpOnMainThread() override { + // Enable downloads resumption. + base::FeatureList::ClearInstanceForTesting(); + scoped_ptr feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine(features::kDownloadResumption.name, + std::string()); + base::FeatureList::SetInstance(std::move(feature_list)); + ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); test_delegate_.reset(new TestShellDownloadManagerDelegate()); @@ -575,6 +577,10 @@ class DownloadContentTest : public ContentBrowserTest { return test_delegate_.get(); } + const base::FilePath& GetDownloadDirectory() const { + return downloads_directory_.path(); + } + // Create a DownloadTestObserverTerminal that will wait for the // specified number of downloads to finish. DownloadTestObserver* CreateWaiter( @@ -602,6 +608,12 @@ class DownloadContentTest : public ContentBrowserTest { .WaitForEvent(); } + void WaitForCancel(DownloadItem* download) { + DownloadUpdatedObserver( + download, base::Bind(&IsDownloadInState, DownloadItem::CANCELLED)) + .WaitForEvent(); + } + // Note: Cannot be used with other alternative DownloadFileFactorys void SetupEnsureNoPendingDownloads() { DownloadManagerForShell(shell())->SetDownloadFileFactoryForTesting( @@ -705,71 +717,6 @@ class DownloadContentTest : public ContentBrowserTest { scoped_ptr test_delegate_; }; -// Parameters for DownloadResumptionContentTest. -enum class DownloadResumptionTestType { - RESUME_WITH_RENDERER, // Resume() is called while the originating WebContents - // is still alive. - RESUME_WITHOUT_RENDERER // Resume() is called after the originating - // WebContents has been deleted. -}; - -// Parameterized test for download resumption. Tests using this fixure will be -// run once with RESUME_WITH_RENDERER and once with RESUME_WITHOUT_RENDERER. -// Use initiator_shell_for_resumption() to retrieve the Shell object that should -// be used as the originator for the initial download. Prior to calling -// Resume(), call PrepareToResume() which will cause the originating Shell to be -// deleted if the test parameter is RESUME_WITHOUT_RENDERER. -class DownloadResumptionContentTest - : public DownloadContentTest, - public ::testing::WithParamInterface { - public: - void SetUpOnMainThread() override { - base::FeatureList::ClearInstanceForTesting(); - scoped_ptr feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - features::kDownloadResumption.name, std::string()); - base::FeatureList::SetInstance(std::move(feature_list)); - - DownloadContentTest::SetUpOnMainThread(); - - if (GetParam() == DownloadResumptionTestType::RESUME_WITHOUT_RENDERER) - initiator_shell_for_resumption_ = CreateBrowser(); - else - initiator_shell_for_resumption_ = shell(); - - ASSERT_EQ(DownloadManagerForShell(shell()), - DownloadManagerForShell(initiator_shell_for_resumption())); - } - - // Shell to use for initiating a download. Only valid *before* - // PrepareToResume() is called. - Shell* initiator_shell_for_resumption() const { - DCHECK(initiator_shell_for_resumption_); - return initiator_shell_for_resumption_; - } - - // Should be called once before calling DownloadItem::Resume() on an - // interrupted download. This may cause initiator_shell_for_resumption() to - // become invalidated. - void PrepareToResume() { - if (GetParam() == DownloadResumptionTestType::RESUME_WITH_RENDERER) - return; - DCHECK_NE(initiator_shell_for_resumption(), shell()); - DCHECK(initiator_shell_for_resumption()); - initiator_shell_for_resumption_->Close(); - initiator_shell_for_resumption_ = nullptr; - } - - private: - Shell* initiator_shell_for_resumption_ = nullptr; -}; - -INSTANTIATE_TEST_CASE_P( - _, - DownloadResumptionContentTest, - ::testing::Values(DownloadResumptionTestType::RESUME_WITH_RENDERER, - DownloadResumptionTestType::RESUME_WITHOUT_RENDERER)); - } // namespace IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadCancelled) { @@ -1070,7 +1017,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ShutdownAtRelease) { } // Test resumption with a response that contains strong validators. -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, StrongValidators) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, StrongValidators) { TestDownloadRequestHandler request_handler; TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); @@ -1078,14 +1025,13 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, StrongValidators) { parameters.injected_errors.front(); request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); ASSERT_EQ(interruption.offset, download->GetReceivedBytes()); ASSERT_EQ(parameters.size, download->GetTotalBytes()); - PrepareToResume(); download->Resume(); WaitForCompletion(download); @@ -1123,13 +1069,200 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, StrongValidators) { value); } +// Resumption should only attempt to contact the final URL if the download has a +// URL chain. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RedirectBeforeResume) { + TestDownloadRequestHandler request_handler_1( + GURL("http://example.com/first-url")); + request_handler_1.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/second-url\r\n" + "\r\n"); + + TestDownloadRequestHandler request_handler_2( + GURL("http://example.com/second-url")); + request_handler_2.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/third-url\r\n" + "\r\n"); + + TestDownloadRequestHandler request_handler_3( + GURL("http://example.com/third-url")); + request_handler_3.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/download\r\n" + "\r\n"); + + TestDownloadRequestHandler resumable_request_handler( + GURL("http://example.com/download")); + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + resumable_request_handler.StartServing(parameters); + + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler_1.url()); + WaitForInterrupt(download); + + EXPECT_EQ(4u, download->GetUrlChain().size()); + EXPECT_EQ(request_handler_1.url(), download->GetOriginalUrl()); + EXPECT_EQ(resumable_request_handler.url(), download->GetURL()); + + // Now that the download is interrupted, make all intermediate servers return + // a 404. The only way a resumption request would succeed if the resumption + // request is sent to the final server in the chain. + const char k404Response[] = "HTTP/1.1 404 Not found\r\n\r\n"; + request_handler_1.StartServingStaticResponse(k404Response); + request_handler_2.StartServingStaticResponse(k404Response); + request_handler_3.StartServingStaticResponse(k404Response); + + download->Resume(); + WaitForCompletion(download); + + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); +} + +// If a resumption request results in a redirect, the response should be ignored +// and the download should be marked as interrupted again. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RedirectWhileResume) { + TestDownloadRequestHandler request_handler( + GURL("http://example.com/first-url")); + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + ++parameters.pattern_generator_seed; + request_handler.StartServing(parameters); + + // We should never send a request to the decoy. If we do, the request will + // always succeed, which results in behavior that diverges from what we want, + // which is for the download to return to being interrupted. + TestDownloadRequestHandler decoy_request_handler( + GURL("http://example.com/decoy")); + decoy_request_handler.StartServing(TestDownloadRequestHandler::Parameters()); + + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); + WaitForInterrupt(download); + + // Upon resumption, the server starts responding with a redirect. This + // response should not be accepted. + request_handler.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/decoy\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE, + download->GetLastReason()); + + // Back to the original request handler. Resumption should now succeed, and + // use the partial data it had prior to the first interruption. + request_handler.StartServing(parameters); + download->Resume(); + WaitForCompletion(download); + + ASSERT_EQ(parameters.size, download->GetReceivedBytes()); + ASSERT_EQ(parameters.size, download->GetTotalBytes()); + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); + + // Characterization risk: The next portion of the test examines the requests + // that were sent out while downloading our resource. These requests + // correspond to the requests that were generated by the browser and the + // downloads system and may change as implementation details change. + TestDownloadRequestHandler::CompletedRequests requests; + request_handler.GetCompletedRequestInfo(&requests); + + ASSERT_EQ(3u, requests.size()); + + // None of the request should have transferred the entire resource. The + // redirect response shows up as a response with 0 bytes transferred. + EXPECT_GT(parameters.size, requests[0].transferred_byte_count); + EXPECT_EQ(0, requests[1].transferred_byte_count); + EXPECT_GT(parameters.size, requests[2].transferred_byte_count); +} + +// If the server response for the resumption request specifies a bad range (i.e. +// not the range that was requested or an invalid or missing Content-Range +// header), then the download should be marked as interrupted again without +// discarding the partial state. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, BadRangeHeader) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + request_handler.StartServing(parameters); + + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); + WaitForInterrupt(download); + + // Upon resumption, the server starts responding with a bad range header. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Content-Range: bytes 1000000-2000000/3000000\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Or this time, the server sends a response with an invalid Content-Range + // header. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Content-Range: ooga-booga-booga-booga\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Or no Content-Range header at all. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Some-Headers: ooga-booga-booga-booga\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Back to the original request handler. Resumption should now succeed, and + // use the partial data it had prior to the first interruption. + request_handler.StartServing(parameters); + download->Resume(); + WaitForCompletion(download); + + ASSERT_EQ(parameters.size, download->GetReceivedBytes()); + ASSERT_EQ(parameters.size, download->GetTotalBytes()); + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); + + // Characterization risk: The next portion of the test examines the requests + // that were sent out while downloading our resource. These requests + // correspond to the requests that were generated by the browser and the + // downloads system and may change as implementation details change. + TestDownloadRequestHandler::CompletedRequests requests; + request_handler.GetCompletedRequestInfo(&requests); + + ASSERT_EQ(5u, requests.size()); + + // None of the request should have transferred the entire resource. + EXPECT_GT(parameters.size, requests[0].transferred_byte_count); + EXPECT_EQ(0, requests[1].transferred_byte_count); + EXPECT_EQ(0, requests[2].transferred_byte_count); + EXPECT_EQ(0, requests[3].transferred_byte_count); + EXPECT_GT(parameters.size, requests[4].transferred_byte_count); +} + // A partial resumption results in an HTTP 200 response. I.e. the server ignored // the range request and sent the entire resource instead. For If-Range requests // (as opposed to If-Match), the behavior for a precondition failure is also to // respond with a 200. So this test case covers both validation failure and // ignoring the range request. -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, - RestartIfNotPartialResponse) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RestartIfNotPartialResponse) { const int kOriginalPatternGeneratorSeed = 1; const int kNewPatternGeneratorSeed = 2; @@ -1142,8 +1275,8 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); ASSERT_EQ(interruption.offset, download->GetReceivedBytes()); @@ -1154,7 +1287,6 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, parameters.pattern_generator_seed = kNewPatternGeneratorSeed; request_handler.StartServing(parameters); - PrepareToResume(); download->Resume(); WaitForCompletion(download); @@ -1192,7 +1324,7 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, } // Confirm we restart if we don't have a verifier. -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoETag) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RestartIfNoETag) { const int kOriginalPatternGeneratorSeed = 1; const int kNewPatternGeneratorSeed = 2; @@ -1204,15 +1336,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoETag) { TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); parameters.pattern_generator_seed = kNewPatternGeneratorSeed; parameters.ClearInjectedErrors(); request_handler.StartServing(parameters); - PrepareToResume(); download->Resume(); WaitForCompletion(download); @@ -1236,14 +1367,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoETag) { // Partial file goes missing before the download is resumed. The download should // restart. -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoPartialFile) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RestartIfNoPartialFile) { TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); // Delete the intermediate file. @@ -1253,7 +1384,6 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoPartialFile) { parameters.ClearInjectedErrors(); request_handler.StartServing(parameters); - PrepareToResume(); download->Resume(); WaitForCompletion(download); @@ -1264,32 +1394,29 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RestartIfNoPartialFile) { download->GetTargetFilePath())); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, - RecoverFromInitFileError) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RecoverFromInitFileError) { TestDownloadRequestHandler request_handler; request_handler.StartServing(TestDownloadRequestHandler::Parameters()); // Setup the error injector. - scoped_refptr injector(TestFileErrorInjector::Create( - DownloadManagerForShell(initiator_shell_for_resumption()))); + scoped_refptr injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); const TestFileErrorInjector::FileErrorInfo err = { - request_handler.url().spec(), TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE}; - injector->AddError(err); - injector->InjectErrors(); + injector->InjectError(err); // Start and watch for interrupt. - DownloadItem* download(StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url())); + DownloadItem* download( + StartDownloadAndReturnItem(shell(), request_handler.url())); WaitForInterrupt(download); ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, download->GetLastReason()); EXPECT_EQ(0, download->GetReceivedBytes()); EXPECT_TRUE(download->GetFullPath().empty()); - EXPECT_TRUE(download->GetTargetFilePath().empty()); + EXPECT_FALSE(download->GetTargetFilePath().empty()); // We need to make sure that any cross-thread downloads communication has // quiesced before clearing and injecting the new errors, as the @@ -1299,35 +1426,31 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RunAllPendingInMessageLoop(); // Clear the old errors list. - injector->ClearErrors(); - injector->InjectErrors(); + injector->ClearError(); // Resume and watch completion. - PrepareToResume(); download->Resume(); WaitForCompletion(download); EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RecoverFromIntermediateFileRenameError) { TestDownloadRequestHandler request_handler; request_handler.StartServing(TestDownloadRequestHandler::Parameters()); // Setup the error injector. - scoped_refptr injector(TestFileErrorInjector::Create( - DownloadManagerForShell(initiator_shell_for_resumption()))); + scoped_refptr injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); const TestFileErrorInjector::FileErrorInfo err = { - request_handler.url().spec(), TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY, 0, DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE}; - injector->AddError(err); - injector->InjectErrors(); + injector->InjectError(err); // Start and watch for interrupt. - DownloadItem* download(StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url())); + DownloadItem* download( + StartDownloadAndReturnItem(shell(), request_handler.url())); WaitForInterrupt(download); ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, @@ -1346,40 +1469,33 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RunAllPendingInMessageLoop(); // Clear the old errors list. - injector->ClearErrors(); - injector->InjectErrors(); + injector->ClearError(); - PrepareToResume(); download->Resume(); WaitForCompletion(download); EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, - RecoverFromFinalRenameError) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RecoverFromFinalRenameError) { TestDownloadRequestHandler request_handler; request_handler.StartServing(TestDownloadRequestHandler::Parameters()); // Setup the error injector. - scoped_refptr injector(TestFileErrorInjector::Create( - DownloadManagerForShell(initiator_shell_for_resumption()))); + scoped_refptr injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); - DownloadManagerForShell(initiator_shell_for_resumption()) - ->RemoveAllDownloads(); + DownloadManagerForShell(shell())->RemoveAllDownloads(); TestFileErrorInjector::FileErrorInfo err = { - request_handler.url().spec(), TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE, 0, - DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE}; - injector->AddError(err); - injector->InjectErrors(); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED}; + injector->InjectError(err); // Start and watch for interrupt. - DownloadItem* download(StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url())); + DownloadItem* download( + StartDownloadAndReturnItem(shell(), request_handler.url())); WaitForInterrupt(download); ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); - EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - download->GetLastReason()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, download->GetLastReason()); EXPECT_TRUE(download->GetFullPath().empty()); // Target path should still be intact. EXPECT_FALSE(download->GetTargetFilePath().empty()); @@ -1392,16 +1508,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RunAllPendingInMessageLoop(); // Clear the old errors list. - injector->ClearErrors(); - injector->InjectErrors(); + injector->ClearError(); - PrepareToResume(); download->Resume(); WaitForCompletion(download); EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, Resume_Hash) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, Resume_Hash) { using InjectedError = TestDownloadRequestHandler::InjectedError; const char kExpectedHash[] = "\xa7\x44\x49\x86\x24\xc6\x84\x6c\x89\xdf\xd8\xec\xa0\xe0\x61\x12\xdc\x80" @@ -1412,8 +1526,8 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, Resume_Hash) { // As a control, let's try GetHash() on an uninterrupted download. request_handler.StartServing(parameters); - DownloadItem* uninterrupted_download(StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url())); + DownloadItem* uninterrupted_download( + StartDownloadAndReturnItem(shell(), request_handler.url())); WaitForCompletion(uninterrupted_download); EXPECT_EQ(expected_hash, uninterrupted_download->GetHash()); @@ -1431,11 +1545,10 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, Resume_Hash) { request_handler.StartServing(parameters); // Start and watch for interrupt. - DownloadItem* download(StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url())); + DownloadItem* download( + StartDownloadAndReturnItem(shell(), request_handler.url())); WaitForInterrupt(download); - PrepareToResume(); download->Resume(); WaitForInterrupt(download); @@ -1456,14 +1569,13 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, Resume_Hash) { // An interrupted download should remove the intermediate file when it is // cancelled. -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, - CancelInterruptedDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelInterruptedDownload) { TestDownloadRequestHandler request_handler; request_handler.StartServing( TestDownloadRequestHandler::Parameters::WithSingleInterruption()); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path = download->GetFullPath(); @@ -1479,14 +1591,13 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, EXPECT_TRUE(download->GetFullPath().empty()); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, - RemoveInterruptedDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveInterruptedDownload) { TestDownloadRequestHandler request_handler; request_handler.StartServing( TestDownloadRequestHandler::Parameters::WithSingleInterruption()); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path = download->GetFullPath(); @@ -1523,14 +1634,14 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveCompletedDownload) { EXPECT_TRUE(base::PathExists(target_path)); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumingDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumingDownload) { TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path(download->GetFullPath()); @@ -1539,15 +1650,13 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumingDownload) { // Resume and remove download. We expect only a single OnDownloadCreated() // call, and that's for the second download created below. - MockDownloadManagerObserver dm_observer( - DownloadManagerForShell(initiator_shell_for_resumption())); + MockDownloadManagerObserver dm_observer(DownloadManagerForShell(shell())); EXPECT_CALL(dm_observer, OnDownloadCreated(_,_)).Times(1); TestRequestStartHandler request_start_handler; parameters.on_start_handler = request_start_handler.GetOnStartHandler(); request_handler.StartServing(parameters); - PrepareToResume(); download->Resume(); request_start_handler.WaitForCallback(); @@ -1575,14 +1684,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumingDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumingDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumingDownload) { TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path(download->GetFullPath()); @@ -1591,15 +1700,13 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumingDownload) { // Resume and cancel download. We expect only a single OnDownloadCreated() // call, and that's for the second download created below. - MockDownloadManagerObserver dm_observer( - DownloadManagerForShell(initiator_shell_for_resumption())); + MockDownloadManagerObserver dm_observer(DownloadManagerForShell(shell())); EXPECT_CALL(dm_observer, OnDownloadCreated(_,_)).Times(1); TestRequestStartHandler request_start_handler; parameters.on_start_handler = request_start_handler.GetOnStartHandler(); request_handler.StartServing(parameters); - PrepareToResume(); download->Resume(); request_start_handler.WaitForCallback(); @@ -1628,14 +1735,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumingDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumedDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumedDownload) { TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path(download->GetFullPath()); @@ -1645,11 +1752,9 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumedDownload) { EXPECT_FALSE(base::PathExists(target_path)); // Resume and remove download. We don't expect OnDownloadCreated() calls. - MockDownloadManagerObserver dm_observer( - DownloadManagerForShell(initiator_shell_for_resumption())); + MockDownloadManagerObserver dm_observer(DownloadManagerForShell(shell())); EXPECT_CALL(dm_observer, OnDownloadCreated(_, _)).Times(0); - PrepareToResume(); download->Resume(); WaitForInProgress(download); @@ -1663,14 +1768,14 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RemoveResumedDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } -IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumedDownload) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumedDownload) { TestDownloadRequestHandler::Parameters parameters = TestDownloadRequestHandler::Parameters::WithSingleInterruption(); TestDownloadRequestHandler request_handler; request_handler.StartServing(parameters); - DownloadItem* download = StartDownloadAndReturnItem( - initiator_shell_for_resumption(), request_handler.url()); + DownloadItem* download = + StartDownloadAndReturnItem(shell(), request_handler.url()); WaitForInterrupt(download); base::FilePath intermediate_path(download->GetFullPath()); @@ -1680,11 +1785,9 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumedDownload) { EXPECT_FALSE(base::PathExists(target_path)); // Resume and remove download. We don't expect OnDownloadCreated() calls. - MockDownloadManagerObserver dm_observer( - DownloadManagerForShell(initiator_shell_for_resumption())); + MockDownloadManagerObserver dm_observer(DownloadManagerForShell(shell())); EXPECT_CALL(dm_observer, OnDownloadCreated(_, _)).Times(0); - PrepareToResume(); download->Resume(); WaitForInProgress(download); @@ -1698,6 +1801,415 @@ IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, CancelResumedDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeRestoredDownload_NoFile) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + parameters.etag, + std::string(), + kIntermediateSize, + parameters.size, + std::string(), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There will be two requests. The first one is issued optimistically assuming + // that the intermediate file exists and matches the size expectations set + // forth in the download metadata (i.e. assuming that a 1331 byte file exists + // at |intermediate_file_path|. + // + // However, once the response is received, DownloadFile will report that the + // intermediate file doesn't exist and hence the download is marked + // interrupted again. + // + // The second request reads the entire entity. + // + // N.b. we can't make any assumptions about how many bytes are transferred by + // the first request since response data will be bufferred until DownloadFile + // is done initializing. + // + // TODO(asanka): Ideally we'll check that the intermediate file matches + // expectations prior to issuing the first resumption request. + ASSERT_EQ(2u, completed_requests.size()); + EXPECT_EQ(parameters.size, completed_requests[1].transferred_byte_count); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeRestoredDownload_NoHash) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + std::vector buffer(kIntermediateSize); + request_handler.GetPatternBytes( + parameters.pattern_generator_seed, 0, buffer.size(), buffer.data()); + ASSERT_EQ( + kIntermediateSize, + base::WriteFile(intermediate_file_path, buffer.data(), buffer.size())); + + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + parameters.etag, + std::string(), + kIntermediateSize, + parameters.size, + std::string(), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There's only one network request issued, and that is for the remainder of + // the file. + ASSERT_EQ(1u, completed_requests.size()); + EXPECT_EQ(parameters.size - kIntermediateSize, + completed_requests[0].transferred_byte_count); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + ResumeRestoredDownload_EtagMismatch) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + std::vector buffer(kIntermediateSize); + request_handler.GetPatternBytes( + parameters.pattern_generator_seed + 1, 0, buffer.size(), buffer.data()); + ASSERT_EQ( + kIntermediateSize, + base::WriteFile(intermediate_file_path, buffer.data(), buffer.size())); + + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + "fake-etag", + std::string(), + kIntermediateSize, + parameters.size, + std::string(), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There's only one network request issued. The If-Range header allows the + // server to respond with the entire entity in one go. The existing contents + // of the file should be discarded, and overwritten by the new contents. + ASSERT_EQ(1u, completed_requests.size()); + EXPECT_EQ(parameters.size, completed_requests[0].transferred_byte_count); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + ResumeRestoredDownload_CorrectHash) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + std::vector buffer(kIntermediateSize); + request_handler.GetPatternBytes( + parameters.pattern_generator_seed, 0, buffer.size(), buffer.data()); + ASSERT_EQ( + kIntermediateSize, + base::WriteFile(intermediate_file_path, buffer.data(), buffer.size())); + // SHA-256 hash of the pattern bytes in buffer. + static const uint8_t kPartialHash[] = { + 0x77, 0x14, 0xfd, 0x83, 0x06, 0x15, 0x10, 0x7a, 0x47, 0x15, 0xd3, + 0xcf, 0xdd, 0x46, 0xa2, 0x61, 0x96, 0xff, 0xc3, 0xbb, 0x49, 0x30, + 0xaf, 0x31, 0x3a, 0x64, 0x0b, 0xd5, 0xfa, 0xb1, 0xe3, 0x81}; + + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + parameters.etag, + std::string(), + kIntermediateSize, + parameters.size, + std::string(std::begin(kPartialHash), std::end(kPartialHash)), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There's only one network request issued, and that is for the remainder of + // the file. + ASSERT_EQ(1u, completed_requests.size()); + EXPECT_EQ(parameters.size - kIntermediateSize, + completed_requests[0].transferred_byte_count); + + // SHA-256 hash of the entire 102400 bytes in the target file. + static const uint8_t kFullHash[] = { + 0xa7, 0x44, 0x49, 0x86, 0x24, 0xc6, 0x84, 0x6c, 0x89, 0xdf, 0xd8, + 0xec, 0xa0, 0xe0, 0x61, 0x12, 0xdc, 0x80, 0x13, 0xf2, 0x83, 0x49, + 0xa9, 0x14, 0x52, 0x32, 0xf0, 0x95, 0x20, 0xca, 0x5b, 0x30}; + EXPECT_EQ(std::string(std::begin(kFullHash), std::end(kFullHash)), + download->GetHash()); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeRestoredDownload_WrongHash) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + std::vector buffer(kIntermediateSize); + ASSERT_EQ( + kIntermediateSize, + base::WriteFile(intermediate_file_path, buffer.data(), buffer.size())); + // SHA-256 hash of the expected pattern bytes in buffer. This doesn't match + // the current contents of the intermediate file which should all be 0. + static const uint8_t kPartialHash[] = { + 0x77, 0x14, 0xfd, 0x83, 0x06, 0x15, 0x10, 0x7a, 0x47, 0x15, 0xd3, + 0xcf, 0xdd, 0x46, 0xa2, 0x61, 0x96, 0xff, 0xc3, 0xbb, 0x49, 0x30, + 0xaf, 0x31, 0x3a, 0x64, 0x0b, 0xd5, 0xfa, 0xb1, 0xe3, 0x81}; + + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + parameters.etag, + std::string(), + kIntermediateSize, + parameters.size, + std::string(std::begin(kPartialHash), std::end(kPartialHash)), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There will be two requests. The first one is issued optimistically assuming + // that the intermediate file exists and matches the size expectations set + // forth in the download metadata (i.e. assuming that a 1331 byte file exists + // at |intermediate_file_path|. + // + // However, once the response is received, DownloadFile will report that the + // intermediate file doesn't match the expected hash. + // + // The second request reads the entire entity. + // + // N.b. we can't make any assumptions about how many bytes are transferred by + // the first request since response data will be bufferred until DownloadFile + // is done initializing. + // + // TODO(asanka): Ideally we'll check that the intermediate file matches + // expectations prior to issuing the first resumption request. + ASSERT_EQ(2u, completed_requests.size()); + EXPECT_EQ(parameters.size, completed_requests[1].transferred_byte_count); + + // SHA-256 hash of the entire 102400 bytes in the target file. + static const uint8_t kFullHash[] = { + 0xa7, 0x44, 0x49, 0x86, 0x24, 0xc6, 0x84, 0x6c, 0x89, 0xdf, 0xd8, + 0xec, 0xa0, 0xe0, 0x61, 0x12, 0xdc, 0x80, 0x13, 0xf2, 0x83, 0x49, + 0xa9, 0x14, 0x52, 0x32, 0xf0, 0x95, 0x20, 0xca, 0x5b, 0x30}; + EXPECT_EQ(std::string(std::begin(kFullHash), std::end(kFullHash)), + download->GetHash()); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeRestoredDownload_ShortFile) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + request_handler.StartServing(parameters); + + base::FilePath intermediate_file_path = + GetDownloadDirectory().AppendASCII("intermediate"); + std::vector url_chain; + + const int kIntermediateSize = 1331; + // Size of file is slightly shorter than the size known to DownloadItem. + std::vector buffer(kIntermediateSize - 100); + request_handler.GetPatternBytes( + parameters.pattern_generator_seed, 0, buffer.size(), buffer.data()); + ASSERT_EQ( + kIntermediateSize - 100, + base::WriteFile(intermediate_file_path, buffer.data(), buffer.size())); + url_chain.push_back(request_handler.url()); + + DownloadItem* download = DownloadManagerForShell(shell())->CreateDownloadItem( + "F7FB1F59-7DE1-4845-AFDB-8A688F70F583", + 1, + intermediate_file_path, + base::FilePath(), + url_chain, + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time(), + parameters.etag, + std::string(), + kIntermediateSize, + parameters.size, + std::string(), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + false); + + download->Resume(); + WaitForCompletion(download); + + EXPECT_FALSE(base::PathExists(intermediate_file_path)); + ReadAndVerifyFileContents(parameters.pattern_generator_seed, + parameters.size, + download->GetTargetFilePath()); + + TestDownloadRequestHandler::CompletedRequests completed_requests; + request_handler.GetCompletedRequestInfo(&completed_requests); + + // There will be two requests. The first one is issued optimistically assuming + // that the intermediate file exists and matches the size expectations set + // forth in the download metadata (i.e. assuming that a 1331 byte file exists + // at |intermediate_file_path|. + // + // However, once the response is received, DownloadFile will report that the + // intermediate file is too short and hence the download is marked interrupted + // again. + // + // The second request reads the entire entity. + // + // N.b. we can't make any assumptions about how many bytes are transferred by + // the first request since response data will be bufferred until DownloadFile + // is done initializing. + // + // TODO(asanka): Ideally we'll check that the intermediate file matches + // expectations prior to issuing the first resumption request. + ASSERT_EQ(2u, completed_requests.size()); + EXPECT_EQ(parameters.size, completed_requests[1].transferred_byte_count); +} + // Check that the cookie policy is correctly updated when downloading a file // that redirects cross origin. IN_PROC_BROWSER_TEST_F(DownloadContentTest, CookiePolicy) { @@ -1834,6 +2346,81 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); } +// A request for a non-existent resource should still result in a DownloadItem +// that's created in an interrupted state. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeServerError) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL download_url = + embedded_test_server()->GetURL("/download/does-not-exist"); + GURL document_url = embedded_test_server()->GetURL( + std::string("/download/download-attribute.html?target=") + + download_url.spec()); + + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); +} + +namespace { + +void ErrorReturningRequestHandler( + const net::HttpRequestHeaders& headers, + const TestDownloadRequestHandler::OnStartResponseCallback& callback) { + callback.Run(std::string(), net::ERR_INTERNET_DISCONNECTED); +} + +} // namespace + +// A request that fails before it gets a response from the server should also +// result in a DownloadItem that's created in an interrupted state. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeNetworkError) { + ASSERT_TRUE(embedded_test_server()->Start()); + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + + parameters.on_start_handler = base::Bind(&ErrorReturningRequestHandler); + request_handler.StartServing(parameters); + + GURL document_url = embedded_test_server()->GetURL( + std::string("/download/download-attribute.html?target=") + + request_handler.url().spec()); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, + download->GetLastReason()); +} + +// A request that fails due to it being rejected by policy should result in a +// DownloadItem that's marked as interrupted. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeInvalidURL) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL document_url = embedded_test_server()->GetURL( + "/download/download-attribute.html?target=about:version"); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + download->GetLastReason()); + EXPECT_FALSE(download->CanResume()); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeBlobURL) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL document_url = + embedded_test_server()->GetURL("/download/download-attribute-blob.html"); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForCompletion(download); + + EXPECT_STREQ(FILE_PATH_LITERAL("suggested-filename.txt"), + download->GetTargetFilePath().BaseName().value().c_str()); +} + // The file empty.bin is served with a MIME type of application/octet-stream. // The content body is empty. Make sure this case is handled properly and we // don't regress on http://crbug.com/320394. @@ -1854,9 +2441,11 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, SniffedMimeType) { EXPECT_TRUE(item->GetOriginalMimeType().empty()); } -IN_PROC_BROWSER_TEST_F(DownloadContentTest, Spam) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DuplicateContentDisposition) { ASSERT_TRUE(embedded_test_server()->Start()); + // double-content-disposition.txt is served with two Content-Disposition + // headers, both of which are identical. NavigateToURLAndWaitForDownload( shell(), embedded_test_server()->GetURL( diff --git a/chromium/content/browser/download/download_create_info.cc b/chromium/content/browser/download/download_create_info.cc index c09d713aa97..92b15849b37 100644 --- a/chromium/content/browser/download/download_create_info.cc +++ b/chromium/content/browser/download/download_create_info.cc @@ -5,7 +5,6 @@ #include "content/browser/download/download_create_info.h" #include -#include #include "base/format_macros.h" #include "base/strings/stringprintf.h" @@ -13,36 +12,24 @@ namespace content { DownloadCreateInfo::DownloadCreateInfo(const base::Time& start_time, - int64_t total_bytes, const net::BoundNetLog& bound_net_log, scoped_ptr save_info) - : start_time(start_time), - total_bytes(total_bytes), - download_id(DownloadItem::kInvalidId), + : download_id(DownloadItem::kInvalidId), + start_time(start_time), + total_bytes(0), has_user_gesture(false), transition_type(ui::PAGE_TRANSITION_LINK), + result(DOWNLOAD_INTERRUPT_REASON_NONE), save_info(std::move(save_info)), request_bound_net_log(bound_net_log) {} DownloadCreateInfo::DownloadCreateInfo() : DownloadCreateInfo(base::Time(), - 0, net::BoundNetLog(), make_scoped_ptr(new DownloadSaveInfo)) {} DownloadCreateInfo::~DownloadCreateInfo() {} -std::string DownloadCreateInfo::DebugString() const { - return base::StringPrintf( - "{" - " download_id = %u" - " url = \"%s\"" - " request_handle = %s" - " total_bytes = %" PRId64 " }", - download_id, url().spec().c_str(), request_handle->DebugString().c_str(), - total_bytes); -} - const GURL& DownloadCreateInfo::url() const { return url_chain.empty() ? GURL::EmptyGURL() : url_chain.back(); } diff --git a/chromium/content/browser/download/download_create_info.h b/chromium/content/browser/download/download_create_info.h index 1767e45eebc..01e1ad5f014 100644 --- a/chromium/content/browser/download/download_create_info.h +++ b/chromium/content/browser/download/download_create_info.h @@ -16,6 +16,7 @@ #include "content/browser/download/download_file.h" #include "content/browser/download/download_request_handle.h" #include "content/common/content_export.h" +#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_save_info.h" #include "net/log/net_log.h" #include "ui/base/page_transition_types.h" @@ -27,18 +28,18 @@ namespace content { // want to pass |DownloadItem|s between threads. struct CONTENT_EXPORT DownloadCreateInfo { DownloadCreateInfo(const base::Time& start_time, - int64_t total_bytes, const net::BoundNetLog& bound_net_log, scoped_ptr save_info); DownloadCreateInfo(); ~DownloadCreateInfo(); - std::string DebugString() const; - // The URL from which we are downloading. This is the final URL after any // redirection by the server for |url_chain|. const GURL& url() const; + // The ID of the download. + uint32_t download_id; + // The chain of redirects that leading up to and including the final URL. std::vector url_chain; @@ -57,14 +58,35 @@ struct CONTENT_EXPORT DownloadCreateInfo { // The total download size. int64_t total_bytes; - // The ID of the download. - uint32_t download_id; - // True if the download was initiated by user action. bool has_user_gesture; ui::PageTransition transition_type; + // The remote IP address where the download was fetched from. Copied from + // UrlRequest::GetSocketAddress(). + std::string remote_address; + + // If the download is initially created in an interrupted state (because the + // response was in error), then |result| would be something other than + // INTERRUPT_REASON_NONE. + DownloadInterruptReason result; + + // The download file save info. + scoped_ptr save_info; + + // The handle to the URLRequest sourcing this download. + scoped_ptr request_handle; + + // The request's |BoundNetLog|, for "source_dependency" linking with the + // download item's. + const net::BoundNetLog request_bound_net_log; + + // --------------------------------------------------------------------------- + // The remaining fields are Entity-body properties. These are only set if + // |result| is DOWNLOAD_INTERRUPT_REASON_NONE. + // --------------------------------------------------------------------------- + // The content-disposition string from the response header. std::string content_disposition; @@ -81,23 +103,9 @@ struct CONTENT_EXPORT DownloadCreateInfo { // "If-Unmodified-Since" comparison. std::string last_modified; - // For continuing a download, the ETAG of the file. + // For continuing a download, the ETag of the file. std::string etag; - // The download file save info. - scoped_ptr save_info; - - // The remote IP address where the download was fetched from. Copied from - // UrlRequest::GetSocketAddress(). - std::string remote_address; - - // The handle to the URLRequest sourcing this download. - scoped_ptr request_handle; - - // The request's |BoundNetLog|, for "source_dependency" linking with the - // download item's. - const net::BoundNetLog request_bound_net_log; - private: DISALLOW_COPY_AND_ASSIGN(DownloadCreateInfo); }; diff --git a/chromium/content/browser/download/download_destination_observer.h b/chromium/content/browser/download/download_destination_observer.h new file mode 100644 index 00000000000..6f76391ecf9 --- /dev/null +++ b/chromium/content/browser/download/download_destination_observer.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 CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_DESTINATION_OBSERVER_H_ +#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_DESTINATION_OBSERVER_H_ + +#include + +#include + +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/download_interrupt_reasons.h" +#include "crypto/secure_hash.h" + +namespace content { + +// Class that receives asynchronous events from a DownloadDestination about +// downloading progress and completion. These should report status when the +// data arrives at its final location; i.e. DestinationUpdate should be +// called after the destination is finished with whatever operation it +// is doing on the data described by |bytes_so_far| and DestinationCompleted +// should only be called once that is true for all data. +// +// All methods are invoked on the UI thread. +// +// Note that this interface does not deal with cross-thread lifetime issues. +class DownloadDestinationObserver { + public: + virtual void DestinationUpdate(int64_t bytes_so_far, + int64_t bytes_per_sec) = 0; + + virtual void DestinationError(DownloadInterruptReason reason, + int64_t bytes_so_far, + scoped_ptr hash_state) = 0; + + virtual void DestinationCompleted( + int64_t total_bytes, + scoped_ptr hash_state) = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_DESTINATION_OBSERVER_H_ diff --git a/chromium/content/browser/download/download_file.h b/chromium/content/browser/download/download_file.h index 2514df15519..c30cab9fe30 100644 --- a/chromium/content/browser/download/download_file.h +++ b/chromium/content/browser/download/download_file.h @@ -14,6 +14,8 @@ #include "content/common/content_export.h" #include "content/public/browser/download_interrupt_reasons.h" +class GURL; + namespace content { class DownloadManager; @@ -55,6 +57,9 @@ class CONTENT_EXPORT DownloadFile { // "Mark of the Web" information about its source. No uniquification // will be performed. virtual void RenameAndAnnotate(const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, const RenameCompletionCallback& callback) = 0; // Detach the file so it is not deleted on destruction. @@ -63,30 +68,8 @@ class CONTENT_EXPORT DownloadFile { // Abort the download and automatically close the file. virtual void Cancel() = 0; - virtual base::FilePath FullPath() const = 0; + virtual const base::FilePath& FullPath() const = 0; virtual bool InProgress() const = 0; - virtual int64_t CurrentSpeed() const = 0; - - // Set |hash| with sha256 digest for the file. - // Returns true if digest is successfully calculated. - virtual bool GetHash(std::string* hash) = 0; - - // Returns the current (intermediate) state of the hash as a byte string. - virtual std::string GetHashState() = 0; - - // Set the application GUID to be used to identify the app to the - // system AV function when scanning downloaded files. Should be called - // before RenameAndAnnotate() to take effect. - virtual void SetClientGuid(const std::string& guid) = 0; - - // For testing. Must be called on FILE thread. - // TODO(rdsmith): Replace use of EnsureNoPendingDownloads() - // on the DownloadManager with a test-specific DownloadFileFactory - // which keeps track of the number of DownloadFiles. - static int GetNumberOfDownloadFiles(); - - protected: - static int number_active_objects_; }; } // namespace content diff --git a/chromium/content/browser/download/download_file_factory.cc b/chromium/content/browser/download/download_file_factory.cc index bae88bef34e..55e03469c89 100644 --- a/chromium/content/browser/download/download_file_factory.cc +++ b/chromium/content/browser/download/download_file_factory.cc @@ -15,15 +15,14 @@ DownloadFileFactory::~DownloadFileFactory() {} DownloadFile* DownloadFileFactory::CreateFile( scoped_ptr save_info, const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr stream, + scoped_ptr byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) { - return new DownloadFileImpl(std::move(save_info), default_downloads_directory, - url, referrer_url, calculate_hash, - std::move(stream), bound_net_log, observer); + return new DownloadFileImpl(std::move(save_info), + default_downloads_directory, + std::move(byte_stream), + bound_net_log, + observer); } } // namespace content diff --git a/chromium/content/browser/download/download_file_factory.h b/chromium/content/browser/download/download_file_factory.h index 2600e3f8455..d29384dde08 100644 --- a/chromium/content/browser/download/download_file_factory.h +++ b/chromium/content/browser/download/download_file_factory.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_FACTORY_H_ #define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_FACTORY_H_ +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -12,6 +13,10 @@ #include "content/common/content_export.h" #include "url/gurl.h" +namespace crypto { +class SecureHash; +} + namespace net { class BoundNetLog; } @@ -31,10 +36,7 @@ class CONTENT_EXPORT DownloadFileFactory { virtual DownloadFile* CreateFile( scoped_ptr save_info, const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr stream, + scoped_ptr byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer); }; diff --git a/chromium/content/browser/download/download_file_impl.cc b/chromium/content/browser/download/download_file_impl.cc index e8e31cee7be..6378b9c8d3b 100644 --- a/chromium/content/browser/download/download_file_impl.cc +++ b/chromium/content/browser/download/download_file_impl.cc @@ -14,11 +14,13 @@ #include "base/values.h" #include "content/browser/byte_stream.h" #include "content/browser/download/download_create_info.h" +#include "content/browser/download/download_destination_observer.h" #include "content/browser/download/download_interrupt_reasons_impl.h" #include "content/browser/download/download_net_log_parameters.h" #include "content/browser/download/download_stats.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/download_destination_observer.h" +#include "crypto/secure_hash.h" +#include "crypto/sha2.h" #include "net/base/io_buffer.h" namespace content { @@ -29,28 +31,19 @@ const int kMaxTimeBlockingFileThreadMs = 1000; // These constants control the default retry behavior for failing renames. Each // retry is performed after a delay that is twice the previous delay. The // initial delay is specified by kInitialRenameRetryDelayMs. -const int kMaxRenameRetries = 3; const int kInitialRenameRetryDelayMs = 200; -int DownloadFile::number_active_objects_ = 0; +// Number of times a failing rename is retried before giving up. +const int kMaxRenameRetries = 3; DownloadFileImpl::DownloadFileImpl( scoped_ptr save_info, const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) - : file_(save_info->file_path, - url, - referrer_url, - save_info->offset, - calculate_hash, - save_info->hash_state, - std::move(save_info->file), - bound_net_log), + : file_(bound_net_log), + save_info_(std::move(save_info)), default_download_directory_(default_download_directory), stream_reader_(std::move(stream)), bytes_seen_(0), @@ -60,7 +53,6 @@ DownloadFileImpl::DownloadFileImpl( DownloadFileImpl::~DownloadFileImpl() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); - --number_active_objects_; } void DownloadFileImpl::Initialize(const InitializeCallback& callback) { @@ -68,7 +60,12 @@ void DownloadFileImpl::Initialize(const InitializeCallback& callback) { update_timer_.reset(new base::RepeatingTimer()); DownloadInterruptReason result = - file_.Initialize(default_download_directory_); + file_.Initialize(save_info_->file_path, + default_download_directory_, + std::move(save_info_->file), + save_info_->offset, + save_info_->hash_of_partial_file, + std::move(save_info_->hash_state)); if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); @@ -89,8 +86,6 @@ void DownloadFileImpl::Initialize(const InitializeCallback& callback) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( callback, DOWNLOAD_INTERRUPT_REASON_NONE)); - - ++number_active_objects_; } DownloadInterruptReason DownloadFileImpl::AppendDataToFile( @@ -109,18 +104,23 @@ DownloadInterruptReason DownloadFileImpl::AppendDataToFile( void DownloadFileImpl::RenameAndUniquify( const base::FilePath& full_path, const RenameCompletionCallback& callback) { - RenameWithRetryInternal( - full_path, UNIQUIFY, kMaxRenameRetries, base::TimeTicks(), callback); + scoped_ptr parameters( + new RenameParameters(UNIQUIFY, full_path, callback)); + RenameWithRetryInternal(std::move(parameters)); } void DownloadFileImpl::RenameAndAnnotate( const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, const RenameCompletionCallback& callback) { - RenameWithRetryInternal(full_path, - ANNOTATE_WITH_SOURCE_INFORMATION, - kMaxRenameRetries, - base::TimeTicks(), - callback); + scoped_ptr parameters(new RenameParameters( + ANNOTATE_WITH_SOURCE_INFORMATION, full_path, callback)); + parameters->client_guid = client_guid; + parameters->source_url = source_url; + parameters->referrer_url = referrer_url; + RenameWithRetryInternal(std::move(parameters)); } base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename( @@ -139,16 +139,12 @@ bool DownloadFileImpl::ShouldRetryFailedRename(DownloadInterruptReason reason) { } void DownloadFileImpl::RenameWithRetryInternal( - const base::FilePath& full_path, - RenameOption option, - int retries_left, - base::TimeTicks time_of_first_failure, - const RenameCompletionCallback& callback) { + scoped_ptr parameters) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); - base::FilePath new_path(full_path); + base::FilePath new_path = parameters->new_path; - if ((option & UNIQUIFY) && full_path != file_.full_path()) { + if ((parameters->option & UNIQUIFY) && new_path != file_.full_path()) { int uniquifier = base::GetUniquePathNumber(new_path, base::FilePath::StringType()); if (uniquifier > 0) @@ -164,36 +160,36 @@ void DownloadFileImpl::RenameWithRetryInternal( // have less assurance that the file at file_.full_path() was the one we were // working with. if (ShouldRetryFailedRename(reason) && file_.in_progress() && - retries_left > 0) { - int attempt_number = kMaxRenameRetries - retries_left; + parameters->retries_left > 0) { + int attempt_number = kMaxRenameRetries - parameters->retries_left; + --parameters->retries_left; + if (parameters->time_of_first_failure.is_null()) + parameters->time_of_first_failure = base::TimeTicks::Now(); BrowserThread::PostDelayedTask( BrowserThread::FILE, FROM_HERE, base::Bind(&DownloadFileImpl::RenameWithRetryInternal, weak_factory_.GetWeakPtr(), - full_path, - option, - --retries_left, - time_of_first_failure.is_null() ? base::TimeTicks::Now() - : time_of_first_failure, - callback), + base::Passed(std::move(parameters))), GetRetryDelayForFailedRename(attempt_number)); return; } - if (!time_of_first_failure.is_null()) + if (!parameters->time_of_first_failure.is_null()) RecordDownloadFileRenameResultAfterRetry( - base::TimeTicks::Now() - time_of_first_failure, reason); + base::TimeTicks::Now() - parameters->time_of_first_failure, reason); if (reason == DOWNLOAD_INTERRUPT_REASON_NONE && - (option & ANNOTATE_WITH_SOURCE_INFORMATION)) { + (parameters->option & ANNOTATE_WITH_SOURCE_INFORMATION)) { // Doing the annotation after the rename rather than before leaves // a very small window during which the file has the final name but // hasn't been marked with the Mark Of The Web. However, it allows // anti-virus scanners on Windows to actually see the data // (http://crbug.com/127999) under the correct name (which is information // it uses). - reason = file_.AnnotateWithSourceInformation(); + reason = file_.AnnotateWithSourceInformation(parameters->client_guid, + parameters->source_url, + parameters->referrer_url); } if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) { @@ -208,8 +204,9 @@ void DownloadFileImpl::RenameWithRetryInternal( } BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(callback, reason, new_path)); + BrowserThread::UI, + FROM_HERE, + base::Bind(parameters->completion_callback, reason, new_path)); } void DownloadFileImpl::Detach() { @@ -220,7 +217,7 @@ void DownloadFileImpl::Cancel() { file_.Cancel(); } -base::FilePath DownloadFileImpl::FullPath() const { +const base::FilePath& DownloadFileImpl::FullPath() const { return file_.full_path(); } @@ -228,22 +225,6 @@ bool DownloadFileImpl::InProgress() const { return file_.in_progress(); } -int64_t DownloadFileImpl::CurrentSpeed() const { - return rate_estimator_.GetCountPerSecond(); -} - -bool DownloadFileImpl::GetHash(std::string* hash) { - return file_.GetHash(hash); -} - -std::string DownloadFileImpl::GetHashState() { - return file_.GetHashState(); -} - -void DownloadFileImpl::SetClientGuid(const std::string& guid) { - file_.SetClientGuid(guid); -} - void DownloadFileImpl::StreamActive() { base::TimeTicks start(base::TimeTicks::Now()); base::TimeTicks now; @@ -280,10 +261,6 @@ void DownloadFileImpl::StreamActive() { stream_reader_->GetStatus()); SendUpdate(); base::TimeTicks close_start(base::TimeTicks::Now()); - if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) - file_.Finish(); - else - file_.FinishWithError(); base::TimeTicks now(base::TimeTicks::Now()); disk_writes_time_ += (now - close_start); RecordFileBandwidth( @@ -321,24 +298,29 @@ void DownloadFileImpl::StreamActive() { // Our observer will clean us up. stream_reader_->RegisterCallback(base::Closure()); weak_factory_.InvalidateWeakPtrs(); - SendUpdate(); // Make info up to date before error. + SendUpdate(); // Make info up to date before error. + scoped_ptr hash_state = file_.Finish(); BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, + BrowserThread::UI, + FROM_HERE, base::Bind(&DownloadDestinationObserver::DestinationError, - observer_, reason)); + observer_, + reason, + file_.bytes_so_far(), + base::Passed(&hash_state))); } else if (state == ByteStreamReader::STREAM_COMPLETE) { // Signal successful completion and shut down processing. stream_reader_->RegisterCallback(base::Closure()); weak_factory_.InvalidateWeakPtrs(); - std::string hash; - if (!GetHash(&hash) || file_.IsEmptyHash(hash)) - hash.clear(); SendUpdate(); + scoped_ptr hash_state = file_.Finish(); BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &DownloadDestinationObserver::DestinationCompleted, - observer_, hash)); + BrowserThread::UI, + FROM_HERE, + base::Bind(&DownloadDestinationObserver::DestinationCompleted, + observer_, + file_.bytes_so_far(), + base::Passed(&hash_state))); } if (bound_net_log_.IsCapturing()) { bound_net_log_.AddEvent( @@ -350,15 +332,23 @@ void DownloadFileImpl::StreamActive() { void DownloadFileImpl::SendUpdate() { BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, + BrowserThread::UI, + FROM_HERE, base::Bind(&DownloadDestinationObserver::DestinationUpdate, - observer_, file_.bytes_so_far(), CurrentSpeed(), - GetHashState())); + observer_, + file_.bytes_so_far(), + rate_estimator_.GetCountPerSecond())); } -// static -int DownloadFile::GetNumberOfDownloadFiles() { - return number_active_objects_; -} +DownloadFileImpl::RenameParameters::RenameParameters( + RenameOption option, + const base::FilePath& new_path, + const RenameCompletionCallback& completion_callback) + : option(option), + new_path(new_path), + retries_left(kMaxRenameRetries), + completion_callback(completion_callback) {} + +DownloadFileImpl::RenameParameters::~RenameParameters() {} } // namespace content diff --git a/chromium/content/browser/download/download_file_impl.h b/chromium/content/browser/download/download_file_impl.h index 98381128505..93b1e551703 100644 --- a/chromium/content/browser/download/download_file_impl.h +++ b/chromium/content/browser/download/download_file_impl.h @@ -10,6 +10,7 @@ #include #include +#include "base/files/file.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -38,15 +39,11 @@ class CONTENT_EXPORT DownloadFileImpl : public DownloadFile { // Note that the DownloadFileImpl automatically reads from the passed in // stream, and sends updates and status of those reads to the // DownloadDestinationObserver. - DownloadFileImpl( - scoped_ptr save_info, - const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr stream, - const net::BoundNetLog& bound_net_log, - base::WeakPtr observer); + DownloadFileImpl(scoped_ptr save_info, + const base::FilePath& default_downloads_directory, + scoped_ptr byte_stream, + const net::BoundNetLog& bound_net_log, + base::WeakPtr observer); ~DownloadFileImpl() override; @@ -55,15 +52,14 @@ class CONTENT_EXPORT DownloadFileImpl : public DownloadFile { void RenameAndUniquify(const base::FilePath& full_path, const RenameCompletionCallback& callback) override; void RenameAndAnnotate(const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, const RenameCompletionCallback& callback) override; void Detach() override; void Cancel() override; - base::FilePath FullPath() const override; + const base::FilePath& FullPath() const override; bool InProgress() const override; - int64_t CurrentSpeed() const override; - bool GetHash(std::string* hash) override; - std::string GetHashState() override; - void SetClientGuid(const std::string& guid) override; protected: // For test class overrides. @@ -85,20 +81,29 @@ class CONTENT_EXPORT DownloadFileImpl : public DownloadFile { ANNOTATE_WITH_SOURCE_INFORMATION = 1 << 1 }; - // Rename file_ to |new_path|. - // |option| specifies additional operations to be performed during the rename. - // See RenameOption above. - // |retries_left| indicates how many times to retry the operation if the - // rename fails with a transient error. - // |time_of_first_failure| Set to an empty base::TimeTicks during the first - // call. Once the first failure is seen, subsequent calls of - // RenameWithRetryInternal will have a non-empty value keeping track of - // the time of first observed failure. Used for UMA. - void RenameWithRetryInternal(const base::FilePath& new_path, - RenameOption option, - int retries_left, - base::TimeTicks time_of_first_failure, - const RenameCompletionCallback& callback); + struct RenameParameters { + RenameParameters(RenameOption option, + const base::FilePath& new_path, + const RenameCompletionCallback& completion_callback); + ~RenameParameters(); + + RenameOption option; + base::FilePath new_path; + std::string client_guid; // See BaseFile::AnnotateWithSourceInformation() + GURL source_url; // See BaseFile::AnnotateWithSourceInformation() + GURL referrer_url; // See BaseFile::AnnotateWithSourceInformation() + int retries_left; // RenameWithRetryInternal() will + // automatically retry until this + // count reaches 0. Each attempt + // decrements this counter. + base::TimeTicks time_of_first_failure; // Set to empty at first, but is set + // when a failure is first + // encountered. Used for UMA. + RenameCompletionCallback completion_callback; + }; + + // Rename file_ based on |parameters|. + void RenameWithRetryInternal(scoped_ptr parameters); // Send an update on our progress. void SendUpdate(); @@ -110,6 +115,11 @@ class CONTENT_EXPORT DownloadFileImpl : public DownloadFile { // The base file instance. BaseFile file_; + // DownloadSaveInfo provided during construction. Since the DownloadFileImpl + // can be created on any thread, this holds the save_info_ until it can be + // used to initialize file_ on the FILE thread. + scoped_ptr save_info_; + // The default directory for creating the download file. base::FilePath default_download_directory_; diff --git a/chromium/content/browser/download/download_file_unittest.cc b/chromium/content/browser/download/download_file_unittest.cc index 9630a49b066..d24eeb81392 100644 --- a/chromium/content/browser/download/download_file_unittest.cc +++ b/chromium/content/browser/download/download_file_unittest.cc @@ -4,7 +4,9 @@ #include #include + #include +#include #include "base/files/file.h" #include "base/files/file_util.h" @@ -18,9 +20,9 @@ #include "content/browser/browser_thread_impl.h" #include "content/browser/byte_stream.h" #include "content/browser/download/download_create_info.h" +#include "content/browser/download/download_destination_observer.h" #include "content/browser/download/download_file_impl.h" #include "content/browser/download/download_request_handle.h" -#include "content/public/browser/download_destination_observer.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_manager.h" #include "content/public/test/mock_download_manager.h" @@ -41,6 +43,14 @@ using ::testing::StrictMock; namespace content { namespace { +std::string GetHexEncodedHashValue(crypto::SecureHash* hash_state) { + if (!hash_state) + return std::string(); + std::vector hash_value(hash_state->GetHashLength()); + hash_state->Finish(&hash_value.front(), hash_value.size()); + return base::HexEncode(&hash_value.front(), hash_value.size()); +} + class MockByteStreamReader : public ByteStreamReader { public: MockByteStreamReader() {} @@ -55,21 +65,33 @@ class MockByteStreamReader : public ByteStreamReader { class MockDownloadDestinationObserver : public DownloadDestinationObserver { public: - MOCK_METHOD3(DestinationUpdate, void(int64_t, int64_t, const std::string&)); - MOCK_METHOD1(DestinationError, void(DownloadInterruptReason)); - MOCK_METHOD1(DestinationCompleted, void(const std::string&)); + MOCK_METHOD2(DestinationUpdate, void(int64_t, int64_t)); + void DestinationError(DownloadInterruptReason reason, + int64_t bytes_so_far, + scoped_ptr hash_state) override { + MockDestinationError( + reason, bytes_so_far, GetHexEncodedHashValue(hash_state.get())); + } + void DestinationCompleted( + int64_t total_bytes, + scoped_ptr hash_state) override { + MockDestinationCompleted(total_bytes, + GetHexEncodedHashValue(hash_state.get())); + } + + MOCK_METHOD3(MockDestinationError, + void(DownloadInterruptReason, int64_t, const std::string&)); + MOCK_METHOD2(MockDestinationCompleted, void(int64_t, const std::string&)); // Doesn't override any methods in the base class. Used to make sure // that the last DestinationUpdate before a Destination{Completed,Error} // had the right values. - MOCK_METHOD3(CurrentUpdateStatus, void(int64_t, int64_t, const std::string&)); + MOCK_METHOD2(CurrentUpdateStatus, void(int64_t, int64_t)); }; MATCHER(IsNullCallback, "") { return (arg.is_null()); } -typedef void (DownloadFile::*DownloadFileRenameMethodType)( - const base::FilePath&, - const DownloadFile::RenameCompletionCallback&); +enum DownloadFileRenameMethodType { RENAME_AND_UNIQUIFY, RENAME_AND_ANNOTATE }; // This is a test DownloadFileImpl that has no retry delay and, on Posix, // retries renames failed due to ACCESS_DENIED. @@ -77,17 +99,11 @@ class TestDownloadFileImpl : public DownloadFileImpl { public: TestDownloadFileImpl(scoped_ptr save_info, const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, scoped_ptr stream, const net::BoundNetLog& bound_net_log, base::WeakPtr observer) : DownloadFileImpl(std::move(save_info), default_downloads_directory, - url, - referrer_url, - calculate_hash, std::move(stream), bound_net_log, observer) {} @@ -111,11 +127,11 @@ class TestDownloadFileImpl : public DownloadFileImpl { class DownloadFileTest : public testing::Test { public: - - static const char* kTestData1; - static const char* kTestData2; - static const char* kTestData3; - static const char* kDataHash; + static const char kTestData1[]; + static const char kTestData2[]; + static const char kTestData3[]; + static const char kDataHash[]; + static const char kEmptyHash[]; static const uint32_t kDummyDownloadId; static const int kDummyChildId; static const int kDummyRequestId; @@ -126,27 +142,23 @@ class DownloadFileTest : public testing::Test { input_stream_(NULL), bytes_(-1), bytes_per_sec_(-1), - hash_state_("xyzzy"), ui_thread_(BrowserThread::UI, &loop_), file_thread_(BrowserThread::FILE, &loop_) { } ~DownloadFileTest() override {} - void SetUpdateDownloadInfo(int64_t bytes, - int64_t bytes_per_sec, - const std::string& hash_state) { + void SetUpdateDownloadInfo(int64_t bytes, int64_t bytes_per_sec) { bytes_ = bytes; bytes_per_sec_ = bytes_per_sec; - hash_state_ = hash_state; } void ConfirmUpdateDownloadInfo() { - observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_); + observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_); } void SetUp() override { - EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) + EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _)) .Times(AnyNumber()) .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); } @@ -176,15 +188,12 @@ class DownloadFileTest : public testing::Test { .RetiresOnSaturation(); scoped_ptr save_info(new DownloadSaveInfo()); - scoped_ptr download_file_impl( - new TestDownloadFileImpl( - std::move(save_info), base::FilePath(), - GURL(), // Source - GURL(), // Referrer - calculate_hash, scoped_ptr(input_stream_), - net::BoundNetLog(), observer_factory_.GetWeakPtr())); - download_file_impl->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC"); - download_file_ = std::move(download_file_impl); + download_file_.reset( + new TestDownloadFileImpl(std::move(save_info), + base::FilePath(), + scoped_ptr(input_stream_), + net::BoundNetLog(), + observer_factory_.GetWeakPtr())); EXPECT_CALL(*input_stream_, Read(_, _)) .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) @@ -269,19 +278,21 @@ class DownloadFileTest : public testing::Test { } void FinishStream(DownloadInterruptReason interrupt_reason, - bool check_observer) { + bool check_observer, + const std::string& expected_hash) { ::testing::Sequence s1; SetupFinishStream(interrupt_reason, s1); sink_callback_.Run(); VerifyStreamAndSize(); if (check_observer) { - EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); + EXPECT_CALL(*(observer_.get()), + MockDestinationCompleted(_, expected_hash)); loop_.RunUntilIdle(); ::testing::Mock::VerifyAndClearExpectations(observer_.get()); - EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) + EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _)) .Times(AnyNumber()) - .WillRepeatedly(Invoke(this, - &DownloadFileTest::SetUpdateDownloadInfo)); + .WillRepeatedly( + Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); } } @@ -289,14 +300,14 @@ class DownloadFileTest : public testing::Test { const base::FilePath& full_path, base::FilePath* result_path_p) { return InvokeRenameMethodAndWaitForCallback( - &DownloadFile::RenameAndUniquify, full_path, result_path_p); + RENAME_AND_UNIQUIFY, full_path, result_path_p); } DownloadInterruptReason RenameAndAnnotate( const base::FilePath& full_path, base::FilePath* result_path_p) { return InvokeRenameMethodAndWaitForCallback( - &DownloadFile::RenameAndAnnotate, full_path, result_path_p); + RENAME_AND_ANNOTATE, full_path, result_path_p); } void ExpectPermissionError(DownloadInterruptReason err) { @@ -306,20 +317,40 @@ class DownloadFileTest : public testing::Test { } protected: + void InvokeRenameMethod( + DownloadFileRenameMethodType method, + const base::FilePath& full_path, + const DownloadFile::RenameCompletionCallback& completion_callback) { + switch (method) { + case RENAME_AND_UNIQUIFY: + download_file_->RenameAndUniquify(full_path, completion_callback); + break; + + case RENAME_AND_ANNOTATE: + download_file_->RenameAndAnnotate( + full_path, + "12345678-ABCD-1234-DCBA-123456789ABC", + GURL(), + GURL(), + completion_callback); + break; + } + } + DownloadInterruptReason InvokeRenameMethodAndWaitForCallback( DownloadFileRenameMethodType method, const base::FilePath& full_path, base::FilePath* result_path_p) { DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE); base::FilePath result_path; - base::RunLoop loop_runner; - ((*download_file_).*method)(full_path, - base::Bind(&DownloadFileTest::SetRenameResult, - base::Unretained(this), - loop_runner.QuitClosure(), - &result_reason, - result_path_p)); + DownloadFile::RenameCompletionCallback completion_callback = + base::Bind(&DownloadFileTest::SetRenameResult, + base::Unretained(this), + loop_runner.QuitClosure(), + &result_reason, + result_path_p); + InvokeRenameMethod(method, full_path, completion_callback); loop_runner.Run(); return result_reason; } @@ -340,7 +371,6 @@ class DownloadFileTest : public testing::Test { // Latest update sent to the observer. int64_t bytes_; int64_t bytes_per_sec_; - std::string hash_state_; base::MessageLoop loop_; @@ -390,15 +420,17 @@ class DownloadFileTestWithRename // the value parameter. INSTANTIATE_TEST_CASE_P(DownloadFile, DownloadFileTestWithRename, - ::testing::Values(&DownloadFile::RenameAndAnnotate, - &DownloadFile::RenameAndUniquify)); + ::testing::Values(RENAME_AND_ANNOTATE, + RENAME_AND_UNIQUIFY)); -const char* DownloadFileTest::kTestData1 = +const char DownloadFileTest::kTestData1[] = "Let's write some data to the file!\n"; -const char* DownloadFileTest::kTestData2 = "Writing more data.\n"; -const char* DownloadFileTest::kTestData3 = "Final line."; -const char* DownloadFileTest::kDataHash = +const char DownloadFileTest::kTestData2[] = "Writing more data.\n"; +const char DownloadFileTest::kTestData3[] = "Final line."; +const char DownloadFileTest::kDataHash[] = "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8"; +const char DownloadFileTest::kEmptyHash[] = + "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"; const uint32_t DownloadFileTest::kDummyDownloadId = 23; const int DownloadFileTest::kDummyChildId = 3; @@ -456,10 +488,7 @@ TEST_P(DownloadFileTestWithRename, RenameFileFinal) { EXPECT_FALSE(base::PathExists(path_2)); EXPECT_TRUE(base::PathExists(path_3)); - // Should not be able to get the hash until the file is closed. - std::string hash; - EXPECT_FALSE(download_file_->GetHash(&hash)); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kDataHash); loop_.RunUntilIdle(); // Rename the file after downloading all the data and closing the file. @@ -473,10 +502,6 @@ TEST_P(DownloadFileTestWithRename, RenameFileFinal) { EXPECT_FALSE(base::PathExists(path_3)); EXPECT_TRUE(base::PathExists(path_4)); - // Check the hash. - EXPECT_TRUE(download_file_->GetHash(&hash)); - EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); - DestroyDownloadFile(0); } @@ -504,7 +529,7 @@ TEST_F(DownloadFileTest, RenameOverwrites) { ASSERT_TRUE(base::ReadFileToString(new_path, &file_contents)); EXPECT_NE(std::string(file_data), file_contents); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); } @@ -529,7 +554,7 @@ TEST_F(DownloadFileTest, RenameUniquifies) { EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL)); EXPECT_TRUE(base::PathExists(path_1_suffixed)); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); } @@ -546,7 +571,7 @@ TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) { RenameAndUniquify(initial_path, &new_path)); EXPECT_TRUE(base::PathExists(initial_path)); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); EXPECT_EQ(initial_path.value(), new_path.value()); @@ -581,7 +606,7 @@ TEST_P(DownloadFileTestWithRename, RenameError) { EXPECT_FALSE(base::PathExists(target_path_suffixed)); } - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); } @@ -644,10 +669,11 @@ TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) { // The Rename() should fail here and enqueue a retry task without invoking // the completion callback. - ((*download_file_).*GetParam())(target_path, - base::Bind(&TestRenameCompletionCallback, - succeeding_run.QuitClosure(), - &did_run_callback)); + InvokeRenameMethod(GetParam(), + target_path, + base::Bind(&TestRenameCompletionCallback, + succeeding_run.QuitClosure(), + &did_run_callback)); EXPECT_FALSE(did_run_callback); base::RunLoop first_failing_run; @@ -673,7 +699,7 @@ TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) { succeeding_run.Run(); EXPECT_TRUE(did_run_callback); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); } @@ -688,10 +714,8 @@ TEST_F(DownloadFileTest, StreamEmptySuccess) { // do anything. AppendDataToFile(NULL, 0); - // Finish the download this way and make sure we see it on the - // observer. - EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false); + // Finish the download this way and make sure we see it on the observer. + FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true, kEmptyHash); loop_.RunUntilIdle(); DestroyDownloadFile(0); @@ -704,9 +728,10 @@ TEST_F(DownloadFileTest, StreamEmptyError) { // Finish the download in error and make sure we see it on the // observer. - EXPECT_CALL(*(observer_.get()), - DestinationError( - DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) + EXPECT_CALL( + *(observer_.get()), + MockDestinationError( + DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, 0, kEmptyHash)) .WillOnce(InvokeWithoutArgs( this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); @@ -715,9 +740,10 @@ TEST_F(DownloadFileTest, StreamEmptyError) { // the last one may have the correct information even if the failure // doesn't produce an update, as the timer update may have triggered at the // same time. - EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _)); + EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _)); - FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false); + FinishStream( + DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false, kEmptyHash); loop_.RunUntilIdle(); @@ -733,7 +759,7 @@ TEST_F(DownloadFileTest, StreamNonEmptySuccess) { ::testing::Sequence s1; SetupDataAppend(chunks1, 2, s1); SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1); - EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); + EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); sink_callback_.Run(); VerifyStreamAndSize(); loop_.RunUntilIdle(); @@ -751,8 +777,8 @@ TEST_F(DownloadFileTest, StreamNonEmptyError) { SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1); EXPECT_CALL(*(observer_.get()), - DestinationError( - DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) + MockDestinationError( + DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, _, _)) .WillOnce(InvokeWithoutArgs( this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); @@ -762,8 +788,7 @@ TEST_F(DownloadFileTest, StreamNonEmptyError) { // doesn't produce an update, as the timer update may have triggered at the // same time. EXPECT_CALL(*(observer_.get()), - CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), - _, _)); + CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), _)); sink_callback_.Run(); loop_.RunUntilIdle(); @@ -771,26 +796,4 @@ TEST_F(DownloadFileTest, StreamNonEmptyError) { DestroyDownloadFile(0); } -// Send some data, wait 3/4s of a second, run the message loop, and -// confirm the values the observer received are correct. -TEST_F(DownloadFileTest, ConfirmUpdate) { - CreateDownloadFile(0, true); - - const char* chunks1[] = { kTestData1, kTestData2 }; - AppendDataToFile(chunks1, 2); - - // Run the message loops for 750ms and check for results. - loop_.task_runner()->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitWhenIdleClosure(), - base::TimeDelta::FromMilliseconds(750)); - loop_.Run(); - - EXPECT_EQ(static_cast(strlen(kTestData1) + strlen(kTestData2)), - bytes_); - EXPECT_EQ(download_file_->GetHashState(), hash_state_); - - FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); - DestroyDownloadFile(0); -} - } // namespace content diff --git a/chromium/content/browser/download/download_interrupt_reasons_impl.cc b/chromium/content/browser/download/download_interrupt_reasons_impl.cc index 2ed8f147ee0..406c4e9d33a 100644 --- a/chromium/content/browser/download/download_interrupt_reasons_impl.cc +++ b/chromium/content/browser/download/download_interrupt_reasons_impl.cc @@ -83,7 +83,8 @@ DownloadInterruptReason ConvertNetErrorToInterruptReason( case net::ERR_TIMED_OUT: return DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT; - // The network connection has been lost. + // The network connection was lost or changed. + case net::ERR_NETWORK_CHANGED: case net::ERR_INTERNET_DISCONNECTED: return DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED; diff --git a/chromium/content/browser/download/download_item_factory.h b/chromium/content/browser/download/download_item_factory.h index 0069e43422e..caf8ef8ce4b 100644 --- a/chromium/content/browser/download/download_item_factory.h +++ b/chromium/content/browser/download/download_item_factory.h @@ -41,6 +41,7 @@ public: virtual DownloadItemImpl* CreatePersistedItem( DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t download_id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -54,6 +55,7 @@ public: const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, diff --git a/chromium/content/browser/download/download_item_impl.cc b/chromium/content/browser/download/download_item_impl.cc index cef57731792..08d90390a60 100644 --- a/chromium/content/browser/download/download_item_impl.cc +++ b/chromium/content/browser/download/download_item_impl.cc @@ -29,15 +29,18 @@ #include "base/bind.h" #include "base/files/file_util.h" #include "base/format_macros.h" +#include "base/guid.h" #include "base/logging.h" #include "base/metrics/histogram.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 "content/browser/download/download_create_info.h" #include "content/browser/download/download_file.h" #include "content/browser/download/download_interrupt_reasons_impl.h" #include "content/browser/download/download_item_impl_delegate.h" +#include "content/browser/download/download_net_log_parameters.h" #include "content/browser/download/download_request_handle.h" #include "content/browser/download/download_stats.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -50,7 +53,6 @@ #include "content/public/browser/download_url_parameters.h" #include "content/public/common/content_features.h" #include "content/public/common/referrer.h" -#include "net/base/net_util.h" namespace content { @@ -99,13 +101,12 @@ bool IsDownloadResumptionEnabled() { const uint32_t DownloadItem::kInvalidId = 0; -const char DownloadItem::kEmptyFileHash[] = ""; - // The maximum number of attempts we will make to resume automatically. const int DownloadItemImpl::kMaxAutoResumeAttempts = 5; // Constructor for reading from the history service. DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t download_id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -119,27 +120,20 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, bool opened, const net::BoundNetLog& bound_net_log) - : is_save_package_download_(false), + : guid_(base::ToUpperASCII(guid)), download_id_(download_id), - current_path_(current_path), target_path_(target_path), - target_disposition_(TARGET_DISPOSITION_OVERWRITE), url_chain_(url_chain), referrer_url_(referrer_url), - transition_type_(ui::PAGE_TRANSITION_LINK), - has_user_gesture_(false), mime_type_(mime_type), original_mime_type_(original_mime_type), total_bytes_(total_bytes), - received_bytes_(received_bytes), - bytes_per_sec_(0), - last_modified_time_(last_modified), - etag_(etag), last_reason_(interrupt_reason), start_tick_(base::TimeTicks()), state_(ExternalToInternalState(state)), @@ -147,20 +141,19 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, start_time_(start_time), end_time_(end_time), delegate_(delegate), - is_paused_(false), - auto_resume_count_(0), - open_when_complete_(false), - file_externally_removed_(false), - auto_opened_(false), - is_temporary_(false), - all_data_saved_(state == COMPLETE), - destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), opened_(opened), - delegate_delayed_complete_(false), + current_path_(current_path), + received_bytes_(received_bytes), + all_data_saved_(state == COMPLETE), + hash_(hash), + last_modified_time_(last_modified), + etag_(etag), bound_net_log_(bound_net_log), weak_ptr_factory_(this) { delegate_->Attach(); - DCHECK_NE(IN_PROGRESS_INTERNAL, state_); + DCHECK(state_ == COMPLETE_INTERNAL || state_ == INTERRUPTED_INTERNAL || + state_ == CANCELLED_INTERNAL); + DCHECK(base::IsValidGUID(guid_)); Init(false /* not actively downloading */, SRC_HISTORY_IMPORT); } @@ -169,7 +162,7 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, uint32_t download_id, const DownloadCreateInfo& info, const net::BoundNetLog& bound_net_log) - : is_save_package_download_(false), + : guid_(base::ToUpperASCII(base::GenerateGUID())), download_id_(download_id), target_disposition_((info.save_info->prompt_for_save_location) ? TARGET_DISPOSITION_PROMPT @@ -187,26 +180,14 @@ DownloadItemImpl::DownloadItemImpl(DownloadItemImplDelegate* delegate, original_mime_type_(info.original_mime_type), remote_address_(info.remote_address), total_bytes_(info.total_bytes), - received_bytes_(0), - bytes_per_sec_(0), - last_modified_time_(info.last_modified), - etag_(info.etag), - last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), + last_reason_(info.result), start_tick_(base::TimeTicks::Now()), - state_(IN_PROGRESS_INTERNAL), - danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), + state_(INITIAL_INTERNAL), start_time_(info.start_time), delegate_(delegate), - is_paused_(false), - auto_resume_count_(0), - open_when_complete_(false), - file_externally_removed_(false), - auto_opened_(false), is_temporary_(!info.save_info->file_path.empty()), - all_data_saved_(false), - destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), - opened_(false), - delegate_delayed_complete_(false), + last_modified_time_(info.last_modified), + etag_(info.etag), bound_net_log_(bound_net_log), weak_ptr_factory_(this) { delegate_->Attach(); @@ -233,35 +214,17 @@ DownloadItemImpl::DownloadItemImpl( const net::BoundNetLog& bound_net_log) : is_save_package_download_(true), request_handle_(std::move(request_handle)), + guid_(base::ToUpperASCII(base::GenerateGUID())), download_id_(download_id), - current_path_(path), target_path_(path), - target_disposition_(TARGET_DISPOSITION_OVERWRITE), url_chain_(1, url), - referrer_url_(GURL()), - transition_type_(ui::PAGE_TRANSITION_LINK), - has_user_gesture_(false), mime_type_(mime_type), original_mime_type_(mime_type), - total_bytes_(0), - received_bytes_(0), - bytes_per_sec_(0), - last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), start_tick_(base::TimeTicks::Now()), state_(IN_PROGRESS_INTERNAL), - danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), start_time_(base::Time::Now()), delegate_(delegate), - is_paused_(false), - auto_resume_count_(0), - open_when_complete_(false), - file_externally_removed_(false), - auto_opened_(false), - is_temporary_(false), - all_data_saved_(false), - destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), - opened_(false), - delegate_delayed_complete_(false), + current_path_(path), bound_net_log_(bound_net_log), weak_ptr_factory_(this) { delegate_->Attach(); @@ -294,6 +257,7 @@ void DownloadItemImpl::RemoveObserver(Observer* observer) { void DownloadItemImpl::UpdateObservers() { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "()"; FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); } @@ -317,7 +281,9 @@ void DownloadItemImpl::ValidateDangerousDownload() { net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED, base::Bind(&ItemCheckedNetLogCallback, GetDangerType())); - UpdateObservers(); + UpdateObservers(); // TODO(asanka): This is potentially unsafe. The download + // may not be in a consistent state or around at all after + // invoking observers. http://crbug.com/586610 MaybeCompleteDownload(); } @@ -327,6 +293,7 @@ void DownloadItemImpl::StealDangerousDownload( DVLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(IsDangerous()); + if (download_file_) { BrowserThread::PostTaskAndReplyWithResult( BrowserThread::FILE, @@ -345,17 +312,50 @@ void DownloadItemImpl::Pause() { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Ignore irrelevant states. - if (state_ != IN_PROGRESS_INTERNAL || is_paused_) + if (is_paused_) return; - request_handle_->PauseRequest(); - is_paused_ = true; - UpdateObservers(); + switch (state_) { + case CANCELLED_INTERNAL: + case COMPLETE_INTERNAL: + case COMPLETING_INTERNAL: + case INITIAL_INTERNAL: + case INTERRUPTED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case RESUMING_INTERNAL: + // No active request. + // TODO(asanka): In the case of RESUMING_INTERNAL, consider setting + // is_paused_ even if there's no request currently associated with this + // DII. When a request is assigned (due to a resumption, for example) we + // can honor the is_paused_ setting. + return; + + case IN_PROGRESS_INTERNAL: + case TARGET_PENDING_INTERNAL: + request_handle_->PauseRequest(); + is_paused_ = true; + UpdateObservers(); + return; + + case MAX_DOWNLOAD_INTERNAL_STATE: + case TARGET_RESOLVED_INTERNAL: + NOTREACHED(); + } } void DownloadItemImpl::Resume() { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); switch (state_) { + case CANCELLED_INTERNAL: // Nothing to resume. + case COMPLETE_INTERNAL: + case COMPLETING_INTERNAL: + case INITIAL_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case RESUMING_INTERNAL: // Resumption in progress. + return; + + case TARGET_PENDING_INTERNAL: case IN_PROGRESS_INTERNAL: if (!is_paused_) return; @@ -364,70 +364,25 @@ void DownloadItemImpl::Resume() { UpdateObservers(); return; - case COMPLETING_INTERNAL: - case COMPLETE_INTERNAL: - case CANCELLED_INTERNAL: - case RESUMING_INTERNAL: - return; - case INTERRUPTED_INTERNAL: auto_resume_count_ = 0; // User input resets the counter. ResumeInterruptedDownload(); + UpdateObservers(); return; case MAX_DOWNLOAD_INTERNAL_STATE: + case TARGET_RESOLVED_INTERNAL: NOTREACHED(); } } void DownloadItemImpl::Cancel(bool user_cancel) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DVLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); - if (state_ != IN_PROGRESS_INTERNAL && - state_ != INTERRUPTED_INTERNAL && - state_ != RESUMING_INTERNAL) { - // Small downloads might be complete before this method has a chance to run. - return; - } - - if (IsDangerous()) { - RecordDangerousDownloadDiscard( - user_cancel ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION - : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN, - GetDangerType(), - GetTargetFilePath()); - } - - last_reason_ = user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED - : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; - - RecordDownloadCount(CANCELLED_COUNT); - - // TODO(rdsmith/benjhayden): Remove condition as part of - // |SavePackage| integration. - // |download_file_| can be NULL if Interrupt() is called after the - // download file has been released. - if (!is_save_package_download_ && download_file_) - ReleaseDownloadFile(true); - - if (state_ == IN_PROGRESS_INTERNAL) { - // Cancel the originating URL request unless it's already been cancelled - // by interrupt. - request_handle_->CancelRequest(); - } - - // Remove the intermediate file if we are cancelling an interrupted download. - // Continuable interruptions leave the intermediate file around. - if ((state_ == INTERRUPTED_INTERNAL || state_ == RESUMING_INTERNAL) && - !current_path_.empty()) { - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(base::IgnoreResult(&DeleteDownloadedFile), current_path_)); - current_path_.clear(); - } - - TransitionTo(CANCELLED_INTERNAL, UPDATE_OBSERVERS); + InterruptAndDiscardPartialState( + user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED + : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN); + UpdateObservers(); } void DownloadItemImpl::Remove() { @@ -435,7 +390,8 @@ void DownloadItemImpl::Remove() { DCHECK_CURRENTLY_ON(BrowserThread::UI); delegate_->AssertStateConsistent(this); - Cancel(true); + InterruptAndDiscardPartialState(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); + UpdateObservers(); delegate_->AssertStateConsistent(this); NotifyRemoved(); @@ -478,6 +434,10 @@ uint32_t DownloadItemImpl::GetId() const { return download_id_; } +const std::string& DownloadItemImpl::GetGuid() const { + return guid_; +} + DownloadItem::DownloadState DownloadItemImpl::GetState() const { return InternalToExternalState(state_); } @@ -495,26 +455,45 @@ bool DownloadItemImpl::IsTemporary() const { } bool DownloadItemImpl::CanResume() const { - if ((GetState() == IN_PROGRESS) && IsPaused()) - return true; - - if (state_ != INTERRUPTED_INTERNAL) - return false; + DCHECK_CURRENTLY_ON(BrowserThread::UI); + switch (state_) { + case INITIAL_INTERNAL: + case COMPLETING_INTERNAL: + case COMPLETE_INTERNAL: + case CANCELLED_INTERNAL: + case RESUMING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + return false; - // We currently only support HTTP(S) requests for download resumption. - if (!GetURL().SchemeIsHTTPOrHTTPS()) - return false; + case TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + case IN_PROGRESS_INTERNAL: + return is_paused_; + + case INTERRUPTED_INTERNAL: { + ResumeMode resume_mode = GetResumeMode(); + // Only allow Resume() calls if the resumption mode requires a user + // action. + return IsDownloadResumptionEnabled() && + (resume_mode == RESUME_MODE_USER_RESTART || + resume_mode == RESUME_MODE_USER_CONTINUE); + } - ResumeMode resume_mode = GetResumeMode(); - return IsDownloadResumptionEnabled() && - (resume_mode == RESUME_MODE_USER_RESTART || - resume_mode == RESUME_MODE_USER_CONTINUE); + case MAX_DOWNLOAD_INTERNAL_STATE: + NOTREACHED(); + } + return false; } bool DownloadItemImpl::IsDone() const { switch (state_) { - case IN_PROGRESS_INTERNAL: + case INITIAL_INTERNAL: case COMPLETING_INTERNAL: + case RESUMING_INTERNAL: + case TARGET_PENDING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + case IN_PROGRESS_INTERNAL: return false; case COMPLETE_INTERNAL: @@ -524,14 +503,10 @@ bool DownloadItemImpl::IsDone() const { case INTERRUPTED_INTERNAL: return !CanResume(); - case RESUMING_INTERNAL: - return false; - case MAX_DOWNLOAD_INTERNAL_STATE: - break; + NOTREACHED(); } - NOTREACHED(); - return true; + return false; } const GURL& DownloadItemImpl::GetURL() const { @@ -628,10 +603,6 @@ const std::string& DownloadItemImpl::GetHash() const { return hash_; } -const std::string& DownloadItemImpl::GetHashState() const { - return hash_state_; -} - bool DownloadItemImpl::GetFileExternallyRemoved() const { return file_externally_removed_; } @@ -770,6 +741,14 @@ WebContents* DownloadItemImpl::GetWebContents() const { void DownloadItemImpl::OnContentCheckCompleted(DownloadDangerType danger_type) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(AllDataSaved()); + + // Danger type is only allowed to be set on an active download after all data + // has been saved. This excludes all other states. In particular, + // OnContentCheckCompleted() isn't allowed on an INTERRUPTED download since + // such an interruption would need to happen between OnAllDataSaved() and + // OnContentCheckCompleted() during which no disk or network activity + // should've taken place. + DCHECK_EQ(state_, IN_PROGRESS_INTERNAL); DVLOG(20) << __FUNCTION__ << " danger_type=" << danger_type << " download=" << DebugString(true); SetDangerType(danger_type); @@ -815,8 +794,7 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { if (verbose) { description += base::StringPrintf( - " total = %" PRId64 - " received = %" PRId64 + " total = %" PRId64 " received = %" PRId64 " reason = %s" " paused = %c" " resume_mode = %s" @@ -827,7 +805,8 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { " etag = '%s'" " has_download_file = %s" " url_chain = \n\t\"%s\"\n\t" - " full_path = \"%" PRFilePath "\"\n\t" + " current_path = \"%" PRFilePath + "\"\n\t" " target_path = \"%" PRFilePath "\"", GetTotalBytes(), GetReceivedBytes(), @@ -854,62 +833,78 @@ std::string DownloadItemImpl::DebugString(bool verbose) const { DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (!IsDownloadResumptionEnabled()) + return RESUME_MODE_INVALID; + + // Only support resumption for HTTP(S). + if (!GetURL().SchemeIsHTTPOrHTTPS()) + return RESUME_MODE_INVALID; + // We can't continue without a handle on the intermediate file. // We also can't continue if we don't have some verifier to make sure // we're getting the same file. - const bool force_restart = + bool restart_required = (current_path_.empty() || (etag_.empty() && last_modified_time_.empty())); // We won't auto-restart if we've used up our attempts or the // download has been paused by user action. - const bool force_user = + bool user_action_required = (auto_resume_count_ >= kMaxAutoResumeAttempts || is_paused_); - ResumeMode mode = RESUME_MODE_INVALID; - switch(last_reason_) { case DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR: case DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT: - if (force_restart && force_user) - mode = RESUME_MODE_USER_RESTART; - else if (force_restart) - mode = RESUME_MODE_IMMEDIATE_RESTART; - else if (force_user) - mode = RESUME_MODE_USER_CONTINUE; - else - mode = RESUME_MODE_IMMEDIATE_CONTINUE; break; case DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE: + // The server disagreed with the file offset that we sent. + + case DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH: + // The file on disk was found to not match the expected hash. Discard and + // start from beginning. + case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT: - if (force_user) - mode = RESUME_MODE_USER_RESTART; - else - mode = RESUME_MODE_IMMEDIATE_RESTART; + // The [possibly persisted] file offset disagreed with the file on disk. + + // The intermediate stub is not usable and the server is responding. Hence + // retrying the request from the beginning is likely to work. + restart_required = true; break; case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: - case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST: case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: + case DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE: case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: case DOWNLOAD_INTERRUPT_REASON_CRASH: - if (force_restart) - mode = RESUME_MODE_USER_RESTART; - else - mode = RESUME_MODE_USER_CONTINUE; + // It is not clear whether attempting a resumption is acceptable at this + // time or whether it would work at all. Hence allow the user to retry the + // download manually. + user_action_required = true; + break; + + case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: + // There was no space. Require user interaction so that the user may, for + // example, choose a different location to store the file. Or they may + // free up some space on the targret device and retry. But try to reuse + // the partial stub. + user_action_required = true; break; case DOWNLOAD_INTERRUPT_REASON_FILE_FAILED: case DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED: - case DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE: case DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG: case DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE: - mode = RESUME_MODE_USER_RESTART; + // Assume the partial stub is unusable. Also it may not be possible to + // restart immediately. + user_action_required = true; + restart_required = true; break; case DOWNLOAD_INTERRUPT_REASON_NONE: + case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST: case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: @@ -918,14 +913,23 @@ DownloadItemImpl::ResumeMode DownloadItemImpl::GetResumeMode() const { case DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED: case DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM: case DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN: - mode = RESUME_MODE_INVALID; - break; + // Unhandled. + return RESUME_MODE_INVALID; } - return mode; + if (user_action_required && restart_required) + return RESUME_MODE_USER_RESTART; + + if (restart_required) + return RESUME_MODE_IMMEDIATE_RESTART; + + if (user_action_required) + return RESUME_MODE_USER_CONTINUE; + + return RESUME_MODE_IMMEDIATE_CONTINUE; } -void DownloadItemImpl::MergeOriginInfoOnResume( +void DownloadItemImpl::UpdateValidatorsOnResumption( const DownloadCreateInfo& new_create_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(RESUMING_INTERNAL, state_); @@ -955,8 +959,7 @@ void DownloadItemImpl::MergeOriginInfoOnResume( origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED; if (content_disposition_ != new_create_info.content_disposition) origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED; - RecordOriginStateOnResumption(new_create_info.save_info->offset != 0, - origin_state); + RecordOriginStateOnResumption(received_bytes_ != 0, origin_state); url_chain_.insert( url_chain_.end(), chain_iter, new_create_info.url_chain.end()); @@ -992,18 +995,20 @@ void DownloadItemImpl::SetTotalBytes(int64_t total_bytes) { total_bytes_ = total_bytes; } -void DownloadItemImpl::OnAllDataSaved(const std::string& final_hash) { +void DownloadItemImpl::OnAllDataSaved( + int64_t total_bytes, + scoped_ptr hash_state) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - - DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); DCHECK(!all_data_saved_); all_data_saved_ = true; - DVLOG(20) << __FUNCTION__ << " download=" << DebugString(true); - - // Store final hash and null out intermediate serialized hash state. - hash_ = final_hash; - hash_state_ = ""; + SetTotalBytes(total_bytes); + UpdateProgress(total_bytes, 0); + SetHashState(std::move(hash_state)); + hash_state_.reset(); // No need to retain hash_state_ since we are done with + // the download and don't expect to receive any more + // data. + DVLOG(20) << __FUNCTION__ << " download=" << DebugString(true); UpdateObservers(); } @@ -1012,38 +1017,28 @@ void DownloadItemImpl::MarkAsComplete() { DCHECK(all_data_saved_); end_time_ = base::Time::Now(); - TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); + TransitionTo(COMPLETE_INTERNAL); + UpdateObservers(); } void DownloadItemImpl::DestinationUpdate(int64_t bytes_so_far, - int64_t bytes_per_sec, - const std::string& hash_state) { + int64_t bytes_per_sec) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DVLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far - << " per_sec=" << bytes_per_sec << " download=" - << DebugString(true); - - if (GetState() != IN_PROGRESS) { - // Ignore if we're no longer in-progress. This can happen if we race a - // Cancel on the UI thread with an update on the FILE thread. - // - // TODO(rdsmith): Arguably we should let this go through, as this means - // the download really did get further than we know before it was - // cancelled. But the gain isn't very large, and the code is more - // fragile if it has to support in progress updates in a non-in-progress - // state. This issue should be readdressed when we revamp performance - // reporting. - return; - } - bytes_per_sec_ = bytes_per_sec; - hash_state_ = hash_state; - received_bytes_ = bytes_so_far; + // If the download is in any other state we don't expect any + // DownloadDestinationObserver callbacks. An interruption or a cancellation + // results in a call to ReleaseDownloadFile which invalidates the weak + // reference held by the DownloadFile and hence cuts off any pending + // callbacks. + DCHECK(state_ == TARGET_PENDING_INTERNAL || state_ == IN_PROGRESS_INTERNAL); - // If we've received more data than we were expecting (bad server info?), - // revert to 'unknown size mode'. - if (received_bytes_ > total_bytes_) - total_bytes_ = 0; + // There must be no pending destination_error_. + DCHECK_EQ(destination_error_, DOWNLOAD_INTERRUPT_REASON_NONE); + DVLOG(20) << __FUNCTION__ << " so_far=" << bytes_so_far + << " per_sec=" << bytes_per_sec + << " download=" << DebugString(true); + + UpdateProgress(bytes_so_far, bytes_per_sec); if (bound_net_log_.IsCapturing()) { bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_UPDATED, @@ -1053,21 +1048,47 @@ void DownloadItemImpl::DestinationUpdate(int64_t bytes_so_far, UpdateObservers(); } -void DownloadItemImpl::DestinationError(DownloadInterruptReason reason) { +void DownloadItemImpl::DestinationError( + DownloadInterruptReason reason, + int64_t bytes_so_far, + scoped_ptr secure_hash) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // If the download is in any other state we don't expect any + // DownloadDestinationObserver callbacks. An interruption or a cancellation + // results in a call to ReleaseDownloadFile which invalidates the weak + // reference held by the DownloadFile and hence cuts off any pending + // callbacks. + DCHECK(state_ == TARGET_PENDING_INTERNAL || state_ == IN_PROGRESS_INTERNAL); + DVLOG(20) << __FUNCTION__ + << "() reason:" << DownloadInterruptReasonToString(reason); + // Postpone recognition of this error until after file name determination // has completed and the intermediate file has been renamed to simplify // resumption conditions. - if (current_path_.empty() || target_path_.empty()) + if (state_ == TARGET_PENDING_INTERNAL) { + received_bytes_ = bytes_so_far; + hash_state_ = std::move(secure_hash); + hash_.clear(); destination_error_ = reason; - else - Interrupt(reason); + return; + } + InterruptWithPartialState(bytes_so_far, std::move(secure_hash), reason); + UpdateObservers(); } -void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) { +void DownloadItemImpl::DestinationCompleted( + int64_t total_bytes, + scoped_ptr secure_hash) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // If the download is in any other state we don't expect any + // DownloadDestinationObserver callbacks. An interruption or a cancellation + // results in a call to ReleaseDownloadFile which invalidates the weak + // reference held by the DownloadFile and hence cuts off any pending + // callbacks. + DCHECK(state_ == TARGET_PENDING_INTERNAL || state_ == IN_PROGRESS_INTERNAL); DVLOG(20) << __FUNCTION__ << " download=" << DebugString(true); - if (GetState() != IN_PROGRESS) - return; - OnAllDataSaved(final_hash); + + OnAllDataSaved(total_bytes, std::move(secure_hash)); MaybeCompleteDownload(); } @@ -1111,24 +1132,79 @@ void DownloadItemImpl::Init(bool active, // We're starting the download. void DownloadItemImpl::Start( scoped_ptr file, - scoped_ptr req_handle) { + scoped_ptr req_handle, + const DownloadCreateInfo& new_create_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!download_file_.get()); - DCHECK(file.get()); - DCHECK(req_handle.get()); + DVLOG(20) << __FUNCTION__ << "() this=" << DebugString(true); download_file_ = std::move(file); request_handle_ = std::move(req_handle); + destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; - if (GetState() == CANCELLED) { + if (state_ == CANCELLED_INTERNAL) { // The download was in the process of resuming when it was cancelled. Don't // proceed. ReleaseDownloadFile(true); - request_handle_->CancelRequest(); + if (request_handle_) + request_handle_->CancelRequest(); + return; + } + + // The state could be one of the following: + // + // INITIAL_INTERNAL: A normal download attempt. + // + // RESUMING_INTERNAL: A resumption attempt. May or may not have been + // successful. + DCHECK(state_ == INITIAL_INTERNAL || state_ == RESUMING_INTERNAL); + + // If the state_ is INITIAL_INTERNAL, then the target path must be empty. + DCHECK(state_ != INITIAL_INTERNAL || target_path_.empty()); + + // If a resumption attempted failed, or if the download was DOA, then the + // download should go back to being interrupted. + if (new_create_info.result != DOWNLOAD_INTERRUPT_REASON_NONE) { + DCHECK(!download_file_.get()); + + // Download requests that are interrupted by Start() should result in a + // DownloadCreateInfo with an intact DownloadSaveInfo. + DCHECK(new_create_info.save_info); + + int64_t offset = new_create_info.save_info->offset; + scoped_ptr hash_state = + make_scoped_ptr(new_create_info.save_info->hash_state + ? new_create_info.save_info->hash_state->Clone() + : nullptr); + + // Interrupted downloads also need a target path. + if (target_path_.empty()) { + received_bytes_ = offset; + hash_state_ = std::move(hash_state); + hash_.clear(); + destination_error_ = new_create_info.result; + TransitionTo(INTERRUPTED_TARGET_PENDING_INTERNAL); + DetermineDownloadTarget(); + return; + } + + // Otherwise, this was a resumption attempt which ended with an + // interruption. Continue with current target path. + TransitionTo(TARGET_RESOLVED_INTERNAL); + InterruptWithPartialState( + offset, std::move(hash_state), new_create_info.result); + UpdateObservers(); return; } - TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS); + // Successful download start. + DCHECK(download_file_.get()); + DCHECK(request_handle_.get()); + + if (state_ == RESUMING_INTERNAL) + UpdateValidatorsOnResumption(new_create_info); + + TransitionTo(TARGET_PENDING_INTERNAL); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, @@ -1142,32 +1218,41 @@ void DownloadItemImpl::Start( void DownloadItemImpl::OnDownloadFileInitialized( DownloadInterruptReason result) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_EQ(state_, TARGET_PENDING_INTERNAL); + DVLOG(20) << __FUNCTION__ + << "() result:" << DownloadInterruptReasonToString(result); if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { - Interrupt(result); - // TODO(rdsmith/asanka): Arguably we should show this in the UI, but - // it's not at all clear what to show--we haven't done filename - // determination, so we don't know what name to display. OTOH, - // the failure mode of not showing the DI if the file initialization - // fails isn't a good one. Can we hack up a name based on the - // URLRequest? We'll need to make sure that initialization happens - // properly. Possibly the right thing is to have the UI handle - // this case specially. - return; + // Whoops. That didn't work. Proceed as an interrupted download, but reset + // the partial state. Currently, the partial stub cannot be recovered if the + // download file initialization fails. + received_bytes_ = 0; + hash_state_.reset(); + hash_.clear(); + destination_error_ = result; + TransitionTo(INTERRUPTED_TARGET_PENDING_INTERNAL); } + DetermineDownloadTarget(); +} + +void DownloadItemImpl::DetermineDownloadTarget() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "() " << DebugString(true); + delegate_->DetermineDownloadTarget( this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, weak_ptr_factory_.GetWeakPtr())); } -// Called by delegate_ when the download target path has been -// determined. +// Called by delegate_ when the download target path has been determined. void DownloadItemImpl::OnDownloadTargetDetermined( const base::FilePath& target_path, TargetDisposition disposition, DownloadDangerType danger_type, const base::FilePath& intermediate_path) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(state_ == TARGET_PENDING_INTERNAL || + state_ == INTERRUPTED_TARGET_PENDING_INTERNAL); // If the |target_path| is empty, then we consider this download to be // canceled. @@ -1176,21 +1261,24 @@ void DownloadItemImpl::OnDownloadTargetDetermined( return; } - // TODO(rdsmith,asanka): We are ignoring the possibility that the download - // has been interrupted at this point until we finish the intermediate - // rename and set the full path. That's dangerous, because we might race - // with resumption, either manual (because the interrupt is visible to the - // UI) or automatic. If we keep the "ignore an error on download until file - // name determination complete" semantics, we need to make sure that the - // error is kept completely invisible until that point. - - DVLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition - << " " << danger_type << " " << DebugString(true); + DVLOG(20) << __FUNCTION__ << "() target_path:" << target_path.value() + << " disposition:" << disposition << " danger_type:" << danger_type + << " this:" << DebugString(true); target_path_ = target_path; target_disposition_ = disposition; SetDangerType(danger_type); + // This was an interrupted download that was looking for a filename. Now that + // it has one, transition to interrupted. + if (state_ == INTERRUPTED_TARGET_PENDING_INTERNAL) { + InterruptWithPartialState( + received_bytes_, std::move(hash_state_), destination_error_); + destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; + UpdateObservers(); + return; + } + // We want the intermediate and target paths to refer to the same directory so // that they are both on the same device and subject to same // space/permission/availability constraints. @@ -1231,27 +1319,38 @@ void DownloadItemImpl::OnDownloadRenamedToIntermediateName( DownloadInterruptReason reason, const base::FilePath& full_path) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_EQ(state_, TARGET_PENDING_INTERNAL); DVLOG(20) << __FUNCTION__ << " download=" << DebugString(true); + TransitionTo(TARGET_RESOLVED_INTERNAL); + + // If the intermediate rename fails while there's also a destination_error_, + // then the former is considered the critical error since it requires + // discarding the partial state. + if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { + // TODO(asanka): Even though the rename failed, it may still be possible to + // recover the partial state from the 'before' name. + InterruptAndDiscardPartialState(reason); + UpdateObservers(); + return; + } + if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) { - // Process destination error. If both |reason| and |destination_error_| - // refer to actual errors, we want to use the |destination_error_| as the - // argument to the Interrupt() routine, as it happened first. - if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) - SetFullPath(full_path); - Interrupt(destination_error_); - destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; - } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { - Interrupt(reason); - // All file errors result in file deletion above; no need to cleanup. The - // current_path_ should be empty. Resuming this download will force a - // restart and a re-doing of filename determination. - DCHECK(current_path_.empty()); - } else { SetFullPath(full_path); + InterruptWithPartialState( + received_bytes_, std::move(hash_state_), destination_error_); + destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; UpdateObservers(); - MaybeCompleteDownload(); + return; } + + SetFullPath(full_path); + TransitionTo(IN_PROGRESS_INTERNAL); + // TODO(asanka): Calling UpdateObservers() prior to MaybeCompleteDownload() is + // not safe. The download could be in an underminate state after invoking + // observers. http://crbug.com/586610 + UpdateObservers(); + MaybeCompleteDownload(); } // When SavePackage downloads MHTML to GData (see @@ -1272,12 +1371,8 @@ void DownloadItemImpl::MaybeCompleteDownload() { weak_ptr_factory_.GetWeakPtr()))) return; - // TODO(rdsmith): DCHECK that we only pass through this point - // once per download. The natural way to do this is by a state - // transition on the DownloadItem. - - // Confirm we're in the proper set of states to be here; - // have all data, have a history handle, (validated or safe). + // Confirm we're in the proper set of states to be here; have all data, have a + // history handle, (validated or safe). DCHECK_EQ(IN_PROGRESS_INTERNAL, state_); DCHECK(!IsDangerous()); DCHECK(all_data_saved_); @@ -1301,9 +1396,8 @@ void DownloadItemImpl::OnDownloadCompleting() { // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration. if (is_save_package_download_) { // Avoid doing anything on the file thread; there's nothing we control - // there. - // Strictly speaking, this skips giving the embedder a chance to open - // the download. But on a save package download, there's no real + // there. Strictly speaking, this skips giving the embedder a chance to + // open the download. But on a save package download, there's no real // concept of opening. Completed(); return; @@ -1316,10 +1410,15 @@ void DownloadItemImpl::OnDownloadCompleting() { base::Bind(&DownloadItemImpl::OnDownloadRenamedToFinalName, weak_ptr_factory_.GetWeakPtr()); BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, + BrowserThread::FILE, + FROM_HERE, base::Bind(&DownloadFile::RenameAndAnnotate, base::Unretained(download_file_.get()), - GetTargetFilePath(), callback)); + GetTargetFilePath(), + delegate_->GetApplicationClientIdForFileScanning(), + GetURL(), + GetReferrerUrl(), + callback)); } void DownloadItemImpl::OnDownloadRenamedToFinalName( @@ -1339,11 +1438,11 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName( << " " << DebugString(false); if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { - Interrupt(reason); - - // All file errors should have resulted in in file deletion above. On - // resumption we will need to re-do filename determination. - DCHECK(current_path_.empty()); + // Failure to perform the final rename is considered fatal. TODO(asanka): It + // may not be, in which case we should figure out whether we can recover the + // state. + InterruptAndDiscardPartialState(reason); + UpdateObservers(); return; } @@ -1356,14 +1455,14 @@ void DownloadItemImpl::OnDownloadRenamedToFinalName( } // Complete the download and release the DownloadFile. - DCHECK(download_file_.get()); + DCHECK(download_file_); ReleaseDownloadFile(false); // We're not completely done with the download item yet, but at this // point we're committed to complete the download. Cancels (or Interrupts, // though it's not clear how they could happen) after this point will be // ignored. - TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS); + TransitionTo(COMPLETING_INTERNAL); if (delegate_->ShouldOpenDownload( this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened, @@ -1389,7 +1488,7 @@ void DownloadItemImpl::Completed() { DCHECK(all_data_saved_); end_time_ = base::Time::Now(); - TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); + TransitionTo(COMPLETE_INTERNAL); RecordDownloadCompleted(start_tick_, received_bytes_); if (auto_opened_) { @@ -1404,32 +1503,28 @@ void DownloadItemImpl::Completed() { OpenDownload(); auto_opened_ = true; - UpdateObservers(); } -} - -void DownloadItemImpl::OnResumeRequestStarted( - DownloadItem* item, - DownloadInterruptReason interrupt_reason) { - // If |item| is not NULL, then Start() has been called already, and nothing - // more needs to be done here. - if (item) { - DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); - DCHECK_EQ(static_cast(this), item); - return; - } - // Otherwise, the request failed without passing through - // DownloadResourceHandler::OnResponseStarted. - DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); - Interrupt(interrupt_reason); + UpdateObservers(); } // **** End of Download progression cascade -// An error occurred somewhere. -void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { +void DownloadItemImpl::InterruptAndDiscardPartialState( + DownloadInterruptReason reason) { + InterruptWithPartialState(0, scoped_ptr(), reason); +} + +void DownloadItemImpl::InterruptWithPartialState( + int64_t bytes_so_far, + scoped_ptr hash_state, + DownloadInterruptReason reason) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason); + DVLOG(20) << __FUNCTION__ + << "() reason:" << DownloadInterruptReasonToString(reason) + << " bytes_so_far:" << bytes_so_far + << " hash_state:" << (hash_state ? "Valid" : "Invalid") + << " this=" << DebugString(true); // Somewhat counter-intuitively, it is possible for us to receive an // interrupt after we've already been interrupted. The generation of @@ -1439,56 +1534,130 @@ void DownloadItemImpl::Interrupt(DownloadInterruptReason reason) { // respectively), and since we choose not to keep state on the File thread, // this is the place where the races collide. It's also possible for // interrupts to race with cancels. + switch (state_) { + case CANCELLED_INTERNAL: + // If the download is already cancelled, then there's no point in + // transitioning out to interrupted. + case COMPLETING_INTERNAL: + case COMPLETE_INTERNAL: + // Already complete. + return; - // Whatever happens, the first one to hit the UI thread wins. - if (state_ != IN_PROGRESS_INTERNAL && state_ != RESUMING_INTERNAL) - return; + case INITIAL_INTERNAL: + case MAX_DOWNLOAD_INTERNAL_STATE: + NOTREACHED(); + return; - last_reason_ = reason; + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case IN_PROGRESS_INTERNAL: + case TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + // last_reason_ needs to be set for GetResumeMode() to work. + last_reason_ = reason; + + if (download_file_) { + ResumeMode resume_mode = GetResumeMode(); + ReleaseDownloadFile(resume_mode != RESUME_MODE_IMMEDIATE_CONTINUE && + resume_mode != RESUME_MODE_USER_CONTINUE); + } + break; - ResumeMode resume_mode = GetResumeMode(); + case RESUMING_INTERNAL: + case INTERRUPTED_INTERNAL: + DCHECK(!download_file_); + // The first non-cancel interrupt reason wins in cases where multiple + // things go wrong. + if (reason != DOWNLOAD_INTERRUPT_REASON_USER_CANCELED && + reason != DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN) + return; - if (state_ == IN_PROGRESS_INTERNAL) { - // Cancel (delete file) if: - // 1) we're going to restart. - // 2) Resumption isn't possible (download was cancelled or blocked due to - // security restrictions). - // 3) Resumption isn't enabled. - // No point in leaving data around we aren't going to use. - ReleaseDownloadFile(resume_mode == RESUME_MODE_IMMEDIATE_RESTART || - resume_mode == RESUME_MODE_USER_RESTART || - resume_mode == RESUME_MODE_INVALID || - !IsDownloadResumptionEnabled()); + last_reason_ = reason; + if (!current_path_.empty()) { + // There is no download file and this is transitioning from INTERRUPTED + // to CANCELLED. The intermediate file is no longer usable, and should + // be deleted. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(base::IgnoreResult(&DeleteDownloadedFile), + current_path_)); + current_path_.clear(); + } + break; + } - // Cancel the originating URL request. - request_handle_->CancelRequest(); + // Reset all data saved, as even if we did save all the data we're going to go + // through another round of downloading when we resume. There's a potential + // problem here in the abstract, as if we did download all the data and then + // run into a continuable error, on resumption we won't download any more + // data. However, a) there are currently no continuable errors that can occur + // after we download all the data, and b) if there were, that would probably + // simply result in a null range request, which would generate a + // DestinationCompleted() notification from the DownloadFile, which would + // behave properly with setting all_data_saved_ to false here. + all_data_saved_ = false; + + if (current_path_.empty()) { + hash_state_.reset(); + hash_.clear(); + received_bytes_ = 0; } else { - DCHECK(!download_file_.get()); + UpdateProgress(bytes_so_far, 0); + SetHashState(std::move(hash_state)); } - // Reset all data saved, as even if we did save all the data we're going - // to go through another round of downloading when we resume. - // There's a potential problem here in the abstract, as if we did download - // all the data and then run into a continuable error, on resumption we - // won't download any more data. However, a) there are currently no - // continuable errors that can occur after we download all the data, and - // b) if there were, that would probably simply result in a null range - // request, which would generate a DestinationCompleted() notification - // from the DownloadFile, which would behave properly with setting - // all_data_saved_ to false here. - all_data_saved_ = false; + if (request_handle_) + request_handle_->CancelRequest(); + + if (reason == DOWNLOAD_INTERRUPT_REASON_USER_CANCELED || + reason == DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN) { + if (IsDangerous()) { + RecordDangerousDownloadDiscard( + reason == DOWNLOAD_INTERRUPT_REASON_USER_CANCELED + ? DOWNLOAD_DISCARD_DUE_TO_USER_ACTION + : DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN, + GetDangerType(), GetTargetFilePath()); + } + + RecordDownloadCount(CANCELLED_COUNT); + TransitionTo(CANCELLED_INTERNAL); + return; + } - TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS); RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); if (!GetWebContents()) RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS); + TransitionTo(INTERRUPTED_INTERNAL); AutoResumeIfValid(); - UpdateObservers(); +} + +void DownloadItemImpl::UpdateProgress(int64_t bytes_so_far, + int64_t bytes_per_sec) { + received_bytes_ = bytes_so_far; + bytes_per_sec_ = bytes_per_sec; + + // If we've received more data than we were expecting (bad server info?), + // revert to 'unknown size mode'. + if (received_bytes_ > total_bytes_) + total_bytes_ = 0; +} + +void DownloadItemImpl::SetHashState(scoped_ptr hash_state) { + hash_state_ = std::move(hash_state); + if (!hash_state_) { + hash_.clear(); + return; + } + + scoped_ptr clone_of_hash_state(hash_state_->Clone()); + std::vector hash_value(clone_of_hash_state->GetHashLength()); + clone_of_hash_state->Finish(&hash_value.front(), hash_value.size()); + hash_.assign(hash_value.begin(), hash_value.end()); } void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "() destroy_file:" << destroy_file; if (destroy_file) { BrowserThread::PostTask( @@ -1514,6 +1683,11 @@ void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) { bool DownloadItemImpl::IsDownloadReadyForCompletion( const base::Closure& state_change_notification) { + // If the download hasn't progressed to the IN_PROGRESS state, then it's not + // ready for completion. + if (state_ != IN_PROGRESS_INTERNAL) + return false; + // If we don't have all the data, the download is not ready for // completion. if (!AllDataSaved()) @@ -1524,20 +1698,13 @@ bool DownloadItemImpl::IsDownloadReadyForCompletion( if (IsDangerous()) return false; - // If the download isn't active (e.g. has been cancelled) it's not - // ready for completion. - if (state_ != IN_PROGRESS_INTERNAL) - return false; - - // If the target filename hasn't been determined, then it's not ready for - // completion. This is checked in ReadyForDownloadCompletionDone(). - if (GetTargetFilePath().empty()) - return false; - - // This is checked in NeedsRename(). Without this conditional, - // browser_tests:DownloadTest.DownloadMimeType fails the DCHECK. - if (target_path_.DirName() != current_path_.DirName()) - return false; + // Check for consistency before invoking delegate. Since there are no pending + // target determination calls and the download is in progress, both the target + // and current paths should be non-empty and they should point to the same + // directory. + DCHECK(!target_path_.empty()); + DCHECK(!current_path_.empty()); + DCHECK(target_path_.DirName() == current_path_.DirName()); // Give the delegate a chance to hold up a stop sign. It'll call // use back through the passed callback if it does and that state changes. @@ -1547,8 +1714,7 @@ bool DownloadItemImpl::IsDownloadReadyForCompletion( return true; } -void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, - ShouldUpdateObservers notify_action) { +void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (state_ == new_state) @@ -1557,49 +1723,89 @@ void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, DownloadInternalState old_state = state_; state_ = new_state; + DCHECK(is_save_package_download_ + ? IsValidSavePackageStateTransition(old_state, new_state) + : IsValidStateTransition(old_state, new_state)) + << "Invalid state transition from:" << DebugDownloadStateString(old_state) + << " to:" << DebugDownloadStateString(new_state); + switch (state_) { + case INITIAL_INTERNAL: + NOTREACHED(); + break; + + case TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + break; + + case IN_PROGRESS_INTERNAL: + DCHECK(!current_path_.empty()) << "Current output path must be known."; + DCHECK(!target_path_.empty()) << "Target path must be known."; + DCHECK(current_path_.DirName() == target_path_.DirName()) + << "Current output directory must match target directory."; + DCHECK(download_file_) << "Output file must be owned by download item."; + DCHECK(request_handle_) << "Download source must be active."; + DCHECK(!is_paused_) << "At the time a download enters IN_PROGRESS state, " + "it must not be paused."; + break; + case COMPLETING_INTERNAL: + DCHECK(all_data_saved_) << "All data must be saved prior to completion."; + DCHECK(!download_file_) + << "Download file must be released prior to completion."; + DCHECK(!target_path_.empty()) << "Target path must be known."; + DCHECK(current_path_ == target_path_) + << "Current output path must match target path."; + bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_COMPLETING, base::Bind(&ItemCompletingNetLogCallback, received_bytes_, &hash_)); break; + case COMPLETE_INTERNAL: bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_FINISHED, base::Bind(&ItemFinishedNetLogCallback, auto_opened_)); break; + case INTERRUPTED_INTERNAL: bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_INTERRUPTED, - base::Bind(&ItemInterruptedNetLogCallback, last_reason_, - received_bytes_, &hash_state_)); + base::Bind( + &ItemInterruptedNetLogCallback, last_reason_, received_bytes_)); break; - case IN_PROGRESS_INTERNAL: - if (old_state == INTERRUPTED_INTERNAL) { - bound_net_log_.AddEvent( - net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED, - base::Bind(&ItemResumingNetLogCallback, - false, last_reason_, received_bytes_, &hash_state_)); - } + + case RESUMING_INTERNAL: + bound_net_log_.AddEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_RESUMED, + base::Bind(&ItemResumingNetLogCallback, + false, + last_reason_, + received_bytes_)); break; + case CANCELLED_INTERNAL: bound_net_log_.AddEvent( net::NetLog::TYPE_DOWNLOAD_ITEM_CANCELED, - base::Bind(&ItemCanceledNetLogCallback, received_bytes_, - &hash_state_)); + base::Bind(&ItemCanceledNetLogCallback, received_bytes_)); break; - default: + + case MAX_DOWNLOAD_INTERNAL_STATE: + NOTREACHED(); break; } - DVLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true) - << " " << InternalToExternalState(old_state) - << " " << InternalToExternalState(state_); + DVLOG(20) << " " << __FUNCTION__ << "()" + << " from:" << DebugDownloadStateString(old_state) + << " to:" << DebugDownloadStateString(state_) + << " this = " << DebugString(true); + bool is_done = + (state_ == COMPLETE_INTERNAL || state_ == INTERRUPTED_INTERNAL || + state_ == RESUMING_INTERNAL || state_ == CANCELLED_INTERNAL); + bool was_done = + (old_state == COMPLETE_INTERNAL || old_state == INTERRUPTED_INTERNAL || + old_state == RESUMING_INTERNAL || old_state == CANCELLED_INTERNAL); - bool is_done = (state_ != IN_PROGRESS_INTERNAL && - state_ != COMPLETING_INTERNAL); - bool was_done = (old_state != IN_PROGRESS_INTERNAL && - old_state != COMPLETING_INTERNAL); // Termination if (is_done && !was_done) bound_net_log_.EndEvent(net::NetLog::TYPE_DOWNLOAD_ITEM_ACTIVE); @@ -1612,9 +1818,6 @@ void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, this, SRC_ACTIVE_DOWNLOAD, &file_name)); } - - if (notify_action == UPDATE_OBSERVERS) - UpdateObservers(); } void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) { @@ -1676,45 +1879,52 @@ void DownloadItemImpl::ResumeInterruptedDownload() { if (state_ != INTERRUPTED_INTERNAL) return; + // We are starting a new request. Shake off all pending operations. + DCHECK(!download_file_); + weak_ptr_factory_.InvalidateWeakPtrs(); + // Reset the appropriate state if restarting. ResumeMode mode = GetResumeMode(); if (mode == RESUME_MODE_IMMEDIATE_RESTART || mode == RESUME_MODE_USER_RESTART) { received_bytes_ = 0; - hash_state_ = ""; - last_modified_time_ = ""; - etag_ = ""; - } - - scoped_ptr download_params; - if (GetWebContents()) { - download_params = - DownloadUrlParameters::FromWebContents(GetWebContents(), GetURL()); - } else { - download_params = make_scoped_ptr(new DownloadUrlParameters( - GetURL(), -1, -1, -1, GetBrowserContext()->GetResourceContext())); + last_modified_time_.clear(); + etag_.clear(); + hash_.clear(); + hash_state_.reset(); } + // Avoid using the WebContents even if it's still around. Resumption requests + // are consistently routed through the no-renderer code paths so that the + // request will not be dropped if the WebContents (and by extension, the + // associated renderer) goes away before a response is received. + scoped_ptr download_params(new DownloadUrlParameters( + GetURL(), -1, -1, -1, GetBrowserContext()->GetResourceContext())); download_params->set_file_path(GetFullPath()); download_params->set_offset(GetReceivedBytes()); - download_params->set_hash_state(GetHashState()); download_params->set_last_modified(GetLastModifiedTime()); download_params->set_etag(GetETag()); - download_params->set_callback( - base::Bind(&DownloadItemImpl::OnResumeRequestStarted, - weak_ptr_factory_.GetWeakPtr())); + download_params->set_hash_of_partial_file(hash_); + download_params->set_hash_state(std::move(hash_state_)); + TransitionTo(RESUMING_INTERNAL); delegate_->ResumeInterruptedDownload(std::move(download_params), GetId()); // Just in case we were interrupted while paused. is_paused_ = false; - - TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS); } // static DownloadItem::DownloadState DownloadItemImpl::InternalToExternalState( DownloadInternalState internal_state) { switch (internal_state) { + case INITIAL_INTERNAL: + case TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + // TODO(asanka): Introduce an externally visible state to distinguish + // between the above states and IN_PROGRESS_INTERNAL. The latter (the + // state where the download is active and has a known target) is the state + // that most external users are interested in. case IN_PROGRESS_INTERNAL: return IN_PROGRESS; case COMPLETING_INTERNAL: @@ -1753,9 +1963,96 @@ DownloadItemImpl::ExternalToInternalState( return MAX_DOWNLOAD_INTERNAL_STATE; } +// static +bool DownloadItemImpl::IsValidSavePackageStateTransition( + DownloadInternalState from, + DownloadInternalState to) { +#if DCHECK_IS_ON() + switch (from) { + case INITIAL_INTERNAL: + case TARGET_PENDING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case TARGET_RESOLVED_INTERNAL: + case COMPLETING_INTERNAL: + case COMPLETE_INTERNAL: + case INTERRUPTED_INTERNAL: + case RESUMING_INTERNAL: + case CANCELLED_INTERNAL: + return false; + + case IN_PROGRESS_INTERNAL: + return to == CANCELLED_INTERNAL || to == COMPLETE_INTERNAL; + + case MAX_DOWNLOAD_INTERNAL_STATE: + NOTREACHED(); + } + return false; +#else + return true; +#endif +} + +// static +bool DownloadItemImpl::IsValidStateTransition(DownloadInternalState from, + DownloadInternalState to) { +#if DCHECK_IS_ON() + switch (from) { + case INITIAL_INTERNAL: + return to == TARGET_PENDING_INTERNAL || + to == INTERRUPTED_TARGET_PENDING_INTERNAL; + + case TARGET_PENDING_INTERNAL: + return to == INTERRUPTED_TARGET_PENDING_INTERNAL || + to == TARGET_RESOLVED_INTERNAL || to == CANCELLED_INTERNAL; + + case INTERRUPTED_TARGET_PENDING_INTERNAL: + return to == INTERRUPTED_INTERNAL || to == CANCELLED_INTERNAL; + + case TARGET_RESOLVED_INTERNAL: + return to == IN_PROGRESS_INTERNAL || to == INTERRUPTED_INTERNAL || + to == CANCELLED_INTERNAL; + + case IN_PROGRESS_INTERNAL: + return to == COMPLETING_INTERNAL || to == CANCELLED_INTERNAL || + to == INTERRUPTED_INTERNAL; + + case COMPLETING_INTERNAL: + return to == COMPLETE_INTERNAL; + + case COMPLETE_INTERNAL: + return false; + + case INTERRUPTED_INTERNAL: + return to == RESUMING_INTERNAL || to == CANCELLED_INTERNAL; + + case RESUMING_INTERNAL: + return to == TARGET_PENDING_INTERNAL || + to == INTERRUPTED_TARGET_PENDING_INTERNAL || + to == TARGET_RESOLVED_INTERNAL || to == CANCELLED_INTERNAL; + + case CANCELLED_INTERNAL: + return false; + + case MAX_DOWNLOAD_INTERNAL_STATE: + NOTREACHED(); + } + return false; +#else + return true; +#endif // DCHECK_IS_ON() +} + const char* DownloadItemImpl::DebugDownloadStateString( DownloadInternalState state) { switch (state) { + case INITIAL_INTERNAL: + return "INITIAL"; + case TARGET_PENDING_INTERNAL: + return "TARGET_PENDING"; + case INTERRUPTED_TARGET_PENDING_INTERNAL: + return "INTERRUPTED_TARGET_PENDING"; + case TARGET_RESOLVED_INTERNAL: + return "TARGET_RESOLVED"; case IN_PROGRESS_INTERNAL: return "IN_PROGRESS"; case COMPLETING_INTERNAL: diff --git a/chromium/content/browser/download/download_item_impl.h b/chromium/content/browser/download/download_item_impl.h index c527861287f..a6afdd86779 100644 --- a/chromium/content/browser/download/download_item_impl.h +++ b/chromium/content/browser/download/download_item_impl.h @@ -16,10 +16,10 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/time/time.h" +#include "content/browser/download/download_destination_observer.h" #include "content/browser/download/download_net_log_parameters.h" #include "content/browser/download/download_request_handle.h" #include "content/common/content_export.h" -#include "content/public/browser/download_destination_observer.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_item.h" #include "net/log/net_log.h" @@ -52,6 +52,7 @@ class CONTENT_EXPORT DownloadItemImpl // Constructing from persistent store: // |bound_net_log| is constructed externally for our use. DownloadItemImpl(DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -65,6 +66,7 @@ class CONTENT_EXPORT DownloadItemImpl const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, @@ -73,6 +75,8 @@ class CONTENT_EXPORT DownloadItemImpl // Constructing for a regular download. // |bound_net_log| is constructed externally for our use. + // TODO(asanka): Get rid of the DownloadCreateInfo parameter since active + // downloads end up at Start() immediately after creation. DownloadItemImpl(DownloadItemImplDelegate* delegate, uint32_t id, const DownloadCreateInfo& info, @@ -103,6 +107,7 @@ class CONTENT_EXPORT DownloadItemImpl void OpenDownload() override; void ShowDownloadInShell() override; uint32_t GetId() const override; + const std::string& GetGuid() const override; DownloadState GetState() const override; DownloadInterruptReason GetLastReason() const override; bool IsPaused() const override; @@ -131,7 +136,6 @@ class CONTENT_EXPORT DownloadItemImpl base::FilePath GetFileNameToReportUser() const override; TargetDisposition GetTargetDisposition() const override; const std::string& GetHash() const override; - const std::string& GetHashState() const override; bool GetFileExternallyRemoved() const override; void DeleteFile(const base::Callback& callback) override; bool IsDangerous() const override; @@ -167,18 +171,18 @@ class CONTENT_EXPORT DownloadItemImpl // INTERRUPTED state. virtual ResumeMode GetResumeMode() const; - // Notify the download item that new origin information is available due to a - // resumption request receiving a response. - virtual void MergeOriginInfoOnResume( - const DownloadCreateInfo& new_create_info); - // State transition operations on regular downloads -------------------------- // Start the download. // |download_file| is the associated file on the storage medium. // |req_handle| is the new request handle associated with the download. + // |new_create_info| is a DownloadCreateInfo containing the new response + // parameters. It may be different from the DownloadCreateInfo used to create + // the DownloadItem if Start() is being called in response for a download + // resumption request. virtual void Start(scoped_ptr download_file, - scoped_ptr req_handle); + scoped_ptr req_handle, + const DownloadCreateInfo& new_create_info); // Needed because of intertwining with DownloadManagerImpl ------------------- @@ -203,94 +207,171 @@ class CONTENT_EXPORT DownloadItemImpl // Called by SavePackage to set the total number of bytes on the item. virtual void SetTotalBytes(int64_t total_bytes); - virtual void OnAllDataSaved(const std::string& final_hash); + virtual void OnAllDataSaved(int64_t total_bytes, + scoped_ptr hash_state); // Called by SavePackage to display progress when the DownloadItem // should be considered complete. virtual void MarkAsComplete(); // DownloadDestinationObserver - void DestinationUpdate(int64_t bytes_so_far, - int64_t bytes_per_sec, - const std::string& hash_state) override; - void DestinationError(DownloadInterruptReason reason) override; - void DestinationCompleted(const std::string& final_hash) override; + void DestinationUpdate(int64_t bytes_so_far, int64_t bytes_per_sec) override; + void DestinationError(DownloadInterruptReason reason, + int64_t bytes_so_far, + scoped_ptr hash_state) override; + void DestinationCompleted(int64_t total_bytes, + scoped_ptr hash_state) override; private: - // Fine grained states of a download. Note that active downloads are created - // in IN_PROGRESS_INTERNAL state. However, downloads creates via history can - // be created in COMPLETE_INTERNAL, CANCELLED_INTERNAL and - // INTERRUPTED_INTERNAL. - + // Fine grained states of a download. + // + // New downloads can be created in the following states: + // + // INITIAL_INTERNAL: All active new downloads. + // + // COMPLETE_INTERNAL: Downloads restored from persisted state. + // CANCELLED_INTERNAL: - do - + // INTERRUPTED_INTERNAL: - do - + // + // IN_PROGRESS_INTERNAL: SavePackage downloads. + // + // On debug builds, state transitions can be verified via + // IsValidStateTransition() and IsValidSavePackageStateTransition(). Allowed + // state transitions are described below, both for normal downloads and + // SavePackage downloads. enum DownloadInternalState { - // Includes both before and after file name determination, and paused - // downloads. - // TODO(rdsmith): Put in state variable for file name determination. - // Transitions from: - // Active downloads are created in this state. - // RESUMING_INTERNAL - // Transitions to: - // COMPLETING_INTERNAL On final rename completion. - // CANCELLED_INTERNAL On cancel. - // INTERRUPTED_INTERNAL On interrupt. - // COMPLETE_INTERNAL On SavePackage download completion. + // Initial state. Regular downloads are created in this state until the + // Start() call is received. + // + // Transitions to (regular): + // TARGET_PENDING_INTERNAL: After a successful Start() call. + // INTERRUPTED_TARGET_PENDING_INTERNAL: After a failed Start() call. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. + INITIAL_INTERNAL, + + // Embedder is in the process of determining the target of the download. + // Since the embedder is sensitive to state transitions during this time, + // any DestinationError/DestinationCompleted events are deferred until + // TARGET_RESOLVED_INTERNAL. + // + // Transitions to (regular): + // TARGET_RESOLVED_INTERNAL: Once the embedder invokes the callback. + // INTERRUPTED_TARGET_PENDING_INTERNAL: An error occurred prior to target + // determination. + // CANCELLED_INTERNAL: Cancelled. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. + TARGET_PENDING_INTERNAL, + + // Embedder is in the process of determining the target of the download, and + // the download is in an interrupted state. The interrupted state is not + // exposed to the emedder until target determination is complete. + // + // Transitions to (regular): + // INTERRUPTED_INTERNAL: Once the target is determined, the download + // is marked as interrupted. + // CANCELLED_INTERNAL: Cancelled. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. + INTERRUPTED_TARGET_PENDING_INTERNAL, + + // Embedder has completed target determination. It is now safe to resolve + // the download target as well as process deferred DestinationError events. + // This state is differs from TARGET_PENDING_INTERNAL due to it being + // allowed to transition to INTERRUPTED_INTERNAL, and it's different from + // IN_PROGRESS_INTERNAL in that entering this state doesn't require having + // a valid target. This state is transient (i.e. DownloadItemImpl will + // transition out of it before yielding execution). It's only purpose in + // life is to ensure the integrity of state transitions. + // + // Transitions to (regular): + // IN_PROGRESS_INTERNAL: Target successfully determined. The incoming + // data stream can now be written to the target. + // INTERRUPTED_INTERNAL: Either the target determination or one of the + // deferred signals indicated that the download + // should be interrupted. + // CANCELLED_INTERNAL: User cancelled the download or there was a + // deferred Cancel() call. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. + TARGET_RESOLVED_INTERNAL, + + // Download target is known and the data can be transferred from our source + // to our sink. + // + // Transitions to (regular): + // COMPLETING_INTERNAL: On final rename completion. + // CANCELLED_INTERNAL: On cancel. + // INTERRUPTED_INTERNAL: On interrupt. + // + // Transitions to (SavePackage): + // COMPLETE_INTERNAL: On completion. + // CANCELLED_INTERNAL: On cancel. IN_PROGRESS_INTERNAL, // Between commit point (dispatch of download file release) and completed. // Embedder may be opening the file in this state. - // Transitions from: - // IN_PROGRESS_INTERNAL - // Transitions to: - // COMPLETE_INTERNAL On successful completion. + // + // Transitions to (regular): + // COMPLETE_INTERNAL: On successful completion. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. COMPLETING_INTERNAL, // After embedder has had a chance to auto-open. User may now open // or auto-open based on extension. - // Transitions from: - // COMPLETING_INTERNAL - // IN_PROGRESS_INTERNAL SavePackage only. - // Completed persisted downloads. - // Transitions to: - // Terminal state. + // + // Transitions to (regular): + // Terminal state. + // + // Transitions to (SavePackage): + // Terminal state. COMPLETE_INTERNAL, - // User has cancelled the download. - // Transitions from: - // IN_PROGRESS_INTERNAL - // INTERRUPTED_INTERNAL - // RESUMING_INTERNAL - // Canceleld persisted downloads. - // Transitions to: - // Terminal state. - CANCELLED_INTERNAL, - // An error has interrupted the download. - // Transitions from: - // IN_PROGRESS_INTERNAL - // RESUMING_INTERNAL - // Interrupted persisted downloads. - // Transitions to: - // RESUMING_INTERNAL On resumption. + // + // Transitions to (regular): + // RESUMING_INTERNAL: On resumption. + // CANCELLED_INTERNAL: On cancel. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. INTERRUPTED_INTERNAL, // A request to resume this interrupted download is in progress. - // Transitions from: - // INTERRUPTED_INTERNAL - // Transitions to: - // IN_PROGRESS_INTERNAL Once a server response is received from a - // resumption. - // INTERRUPTED_INTERNAL If the resumption request fails. - // CANCELLED_INTERNAL On cancel. + // + // Transitions to (regular): + // TARGET_PENDING_INTERNAL: Once a server response is received from a + // resumption. + // INTERRUPTED_TARGET_PENDING_INTERNAL: A server response was received, + // but it indicated an error, and the download + // needs to go through target determination. + // TARGET_RESOLVED_INTERNAL: A resumption attempt received an error + // but it was not necessary to go through target + // determination. + // CANCELLED_INTERNAL: On cancel. + // + // Transitions to (SavePackage): + // SavePackage downloads never reach this state. RESUMING_INTERNAL, - MAX_DOWNLOAD_INTERNAL_STATE, - }; + // User has cancelled the download. + // TODO(asanka): Merge interrupted and cancelled states. + // + // Transitions to (regular): + // Terminal state. + // + // Transitions to (SavePackage): + // Terminal state. + CANCELLED_INTERNAL, - // Used with TransitionTo() to indicate whether or not to call - // UpdateObservers() after the state transition. - enum ShouldUpdateObservers { - UPDATE_OBSERVERS, - DONT_UPDATE_OBSERVERS + MAX_DOWNLOAD_INTERNAL_STATE, }; // Normal progression of a download ------------------------------------------ @@ -305,6 +386,13 @@ class CONTENT_EXPORT DownloadItemImpl // this is. void Init(bool active, DownloadType download_type); + // Callback from file thread when we initialize the DownloadFile. + void OnDownloadFileInitialized(DownloadInterruptReason result); + + // Called to determine the target path. Will cause OnDownloadTargetDetermined + // to be called when the target information is available. + void DetermineDownloadTarget(); + // Called when the target path has been determined. |target_path| is the // suggested target path. |disposition| indicates how the target path should // be used (see TargetDisposition). |danger_type| is the danger level of @@ -316,9 +404,6 @@ class CONTENT_EXPORT DownloadItemImpl DownloadDangerType danger_type, const base::FilePath& intermediate_path); - // Callback from file thread when we initialize the DownloadFile. - void OnDownloadFileInitialized(DownloadInterruptReason result); - void OnDownloadRenamedToIntermediateName( DownloadInterruptReason reason, const base::FilePath& full_path); @@ -339,18 +424,29 @@ class CONTENT_EXPORT DownloadItemImpl // the download has been opened. void DelayedDownloadOpened(bool auto_opened); - // Called when the entire download operation (including renaming etc) + // Called when the entire download operation (including renaming etc.) // is completed. void Completed(); - // Callback invoked when the URLRequest for a download resumption has started. - void OnResumeRequestStarted(DownloadItem* item, - DownloadInterruptReason interrupt_reason); - // Helper routines ----------------------------------------------------------- - // Indicate that an error has occurred on the download. - void Interrupt(DownloadInterruptReason reason); + // Indicate that an error has occurred on the download. Discards partial + // state. The interrupted download will not be considered continuable, but may + // be restarted. + void InterruptAndDiscardPartialState(DownloadInterruptReason reason); + + // Indiates that an error has occurred on the download. The |bytes_so_far| and + // |hash_state| should correspond to the state of the DownloadFile. If the + // interrupt reason allows, this partial state may be allowed to continue the + // interrupted download upon resumption. + void InterruptWithPartialState(int64_t bytes_so_far, + scoped_ptr hash_state, + DownloadInterruptReason reason); + + void UpdateProgress(int64_t bytes_so_far, int64_t bytes_per_sec); + + // Set |hash_| and |hash_state_| based on |hash_state|. + void SetHashState(scoped_ptr hash_state); // Destroy the DownloadFile object. If |destroy_file| is true, the file is // destroyed with it. Otherwise, DownloadFile::Detach() is called before @@ -366,10 +462,9 @@ class CONTENT_EXPORT DownloadItemImpl // Call to transition state; all state transitions should go through this. // |notify_action| specifies whether or not to call UpdateObservers() after // the state transition. - void TransitionTo(DownloadInternalState new_state, - ShouldUpdateObservers notify_action); + void TransitionTo(DownloadInternalState new_state); - // Set the |danger_type_| and invoke obserers if necessary. + // Set the |danger_type_| and invoke observers if necessary. void SetDangerType(DownloadDangerType danger_type); void SetFullPath(const base::FilePath& new_path); @@ -378,6 +473,11 @@ class CONTENT_EXPORT DownloadItemImpl void ResumeInterruptedDownload(); + // Update origin information based on the response to a download resumption + // request. Should only be called if the resumption request was successful. + virtual void UpdateValidatorsOnResumption( + const DownloadCreateInfo& new_create_info); + static DownloadState InternalToExternalState( DownloadInternalState internal_state); static DownloadInternalState ExternalToInternalState( @@ -386,34 +486,34 @@ class CONTENT_EXPORT DownloadItemImpl // Debugging routines -------------------------------------------------------- static const char* DebugDownloadStateString(DownloadInternalState state); static const char* DebugResumeModeString(ResumeMode mode); + static bool IsValidSavePackageStateTransition(DownloadInternalState from, + DownloadInternalState to); + static bool IsValidStateTransition(DownloadInternalState from, + DownloadInternalState to); // Will be false for save package downloads retrieved from the history. // TODO(rdsmith): Replace with a generalized enum for "download source". - const bool is_save_package_download_; + const bool is_save_package_download_ = false; // The handle to the request information. Used for operations outside the // download system. scoped_ptr request_handle_; - uint32_t download_id_; + std::string guid_; + + uint32_t download_id_ = kInvalidId; // Display name for the download. If this is empty, then the display name is // considered to be |target_path_.BaseName()|. base::FilePath display_name_; - // Full path to the downloaded or downloading file. This is the path to the - // physical file, if one exists. The final target path is specified by - // |target_path_|. |current_path_| can be empty if the in-progress path hasn't - // been determined. - base::FilePath current_path_; - // Target path of an in-progress download. We may be downloading to a // temporary or intermediate file (specified by |current_path_|. Once the // download completes, we will rename the file to |target_path_|. base::FilePath target_path_; // Whether the target should be overwritten, uniquified or prompted for. - TargetDisposition target_disposition_; + TargetDisposition target_disposition_ = TARGET_DISPOSITION_OVERWRITE; // The chain of redirects that leading up to and including the final URL. std::vector url_chain_; @@ -437,10 +537,10 @@ class CONTENT_EXPORT DownloadItemImpl base::FilePath forced_file_path_; // Page transition that triggerred the download. - ui::PageTransition transition_type_; + ui::PageTransition transition_type_ = ui::PAGE_TRANSITION_LINK; // Whether the download was triggered with a user gesture. - bool has_user_gesture_; + bool has_user_gesture_ = false; // Information from the request. // Content-disposition field from the header. @@ -459,40 +559,19 @@ class CONTENT_EXPORT DownloadItemImpl std::string remote_address_; // Total bytes expected. - int64_t total_bytes_; - - // Current received bytes. - int64_t received_bytes_; - - // Current speed. Calculated by the DownloadFile. - int64_t bytes_per_sec_; - - // Sha256 hash of the content. This might be empty either because - // the download isn't done yet or because the hash isn't needed - // (ChromeDownloadManagerDelegate::GenerateFileHash() returned false). - std::string hash_; - - // A blob containing the state of the hash algorithm. Only valid while the - // download is in progress. - std::string hash_state_; - - // Server's time stamp for the file. - std::string last_modified_time_; - - // Server's ETAG for the file. - std::string etag_; + int64_t total_bytes_ = 0; // Last reason. - DownloadInterruptReason last_reason_; + DownloadInterruptReason last_reason_ = DOWNLOAD_INTERRUPT_REASON_NONE; // Start time for recording statistics. base::TimeTicks start_tick_; // The current state of this download. - DownloadInternalState state_; + DownloadInternalState state_ = INITIAL_INTERNAL; // Current danger type for the download. - DownloadDangerType danger_type_; + DownloadDangerType danger_type_ = DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS; // The views of this item in the download shelf and download contents. base::ObserverList observers_; @@ -504,44 +583,40 @@ class CONTENT_EXPORT DownloadItemImpl base::Time end_time_; // Our delegate. - DownloadItemImplDelegate* delegate_; + DownloadItemImplDelegate* delegate_ = nullptr; // In progress downloads may be paused by the user, we note it here. - bool is_paused_; - - // The number of times this download has been resumed automatically. - int auto_resume_count_; + bool is_paused_ = false; // A flag for indicating if the download should be opened at completion. - bool open_when_complete_; + bool open_when_complete_ = false; // A flag for indicating if the downloaded file is externally removed. - bool file_externally_removed_; + bool file_externally_removed_ = false; // True if the download was auto-opened. We set this rather than using // an observer as it's frequently possible for the download to be auto opened // before the observer is added. - bool auto_opened_; + bool auto_opened_ = false; // True if the item was downloaded temporarily. - bool is_temporary_; - - // True if we've saved all the data for the download. - bool all_data_saved_; - - // Error return from DestinationError. Stored separately from - // last_reason_ so that we can avoid handling destination errors until - // after file name determination has occurred. - DownloadInterruptReason destination_error_; + bool is_temporary_ = false; // Did the user open the item either directly or indirectly (such as by // setting always open files of this type)? The shelf also sets this field // when the user closes the shelf before the item has been opened but should // be treated as though the user opened it. - bool opened_; + bool opened_ = false; // Did the delegate delay calling Complete on this download? - bool delegate_delayed_complete_; + bool delegate_delayed_complete_ = false; + + // Error return from DestinationError. Stored separately from + // last_reason_ so that we can avoid handling destination errors until + // after file name determination has occurred. + DownloadInterruptReason destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; + + // The following fields describe the current state of the download file. // DownloadFile associated with this download. Note that this // pointer may only be used or destroyed on the FILE thread. @@ -549,6 +624,47 @@ class CONTENT_EXPORT DownloadItemImpl // the IN_PROGRESS state. scoped_ptr download_file_; + // Full path to the downloaded or downloading file. This is the path to the + // physical file, if one exists. The final target path is specified by + // |target_path_|. |current_path_| can be empty if the in-progress path hasn't + // been determined. + base::FilePath current_path_; + + // Current received bytes. + int64_t received_bytes_ = 0; + + // Current speed. Calculated by the DownloadFile. + int64_t bytes_per_sec_ = 0; + + // True if we've saved all the data for the download. If true, then the file + // at |current_path_| contains |received_bytes_|, which constitute the + // entirety of what we expect to save there. A digest of its contents can be + // found at |hash_|. + bool all_data_saved_ = false; + + // The number of times this download has been resumed automatically. Will be + // reset to 0 if a resumption is performed in response to a Resume() call. + int auto_resume_count_ = 0; + + // SHA256 hash of the possibly partial content. The hash is updated each time + // the download is interrupted, and when the all the data has been + // transferred. |hash_| contains the raw binary hash and is not hex encoded. + // + // While the download is in progress, and while resuming, |hash_| will be + // empty. + std::string hash_; + + // In the event of an interruption, the DownloadDestinationObserver interface + // exposes the partial hash state. This state can be held by the download item + // in case it's needed for resumption. + scoped_ptr hash_state_; + + // Contents of the Last-Modified header for the most recent server response. + std::string last_modified_time_; + + // Server's ETAG for the file. + std::string etag_; + // Net log to use for this download. const net::BoundNetLog bound_net_log_; diff --git a/chromium/content/browser/download/download_item_impl_delegate.cc b/chromium/content/browser/download/download_item_impl_delegate.cc index c1ed48cd124..be7432599e1 100644 --- a/chromium/content/browser/download/download_item_impl_delegate.cc +++ b/chromium/content/browser/download/download_item_impl_delegate.cc @@ -56,6 +56,11 @@ bool DownloadItemImplDelegate::ShouldOpenFileBasedOnExtension( void DownloadItemImplDelegate::CheckForFileRemoval( DownloadItemImpl* download_item) {} +std::string DownloadItemImplDelegate::GetApplicationClientIdForFileScanning() + const { + return std::string(); +} + void DownloadItemImplDelegate::ResumeInterruptedDownload( scoped_ptr params, uint32_t id) {} diff --git a/chromium/content/browser/download/download_item_impl_delegate.h b/chromium/content/browser/download/download_item_impl_delegate.h index 1925963f0a2..8dd9d8cb740 100644 --- a/chromium/content/browser/download/download_item_impl_delegate.h +++ b/chromium/content/browser/download/download_item_impl_delegate.h @@ -69,6 +69,11 @@ class CONTENT_EXPORT DownloadItemImplDelegate { // to OnDownloadedFileRemoved(). virtual void CheckForFileRemoval(DownloadItemImpl* download_item); + // Return a GUID string used for identifying the application to the system AV + // function for scanning downloaded files. If an empty or invalid GUID string + // is returned, no client identification will be given to the AV function. + virtual std::string GetApplicationClientIdForFileScanning() const; + // Called when an interrupted download is resumed. virtual void ResumeInterruptedDownload( scoped_ptr params, diff --git a/chromium/content/browser/download/download_item_impl_unittest.cc b/chromium/content/browser/download/download_item_impl_unittest.cc index 0afad1bfca2..45c822cfc55 100644 --- a/chromium/content/browser/download/download_item_impl_unittest.cc +++ b/chromium/content/browser/download/download_item_impl_unittest.cc @@ -5,39 +5,54 @@ #include "content/browser/download/download_item_impl.h" #include + +#include +#include #include #include "base/callback.h" #include "base/feature_list.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/stl_util.h" #include "base/threading/thread.h" #include "content/browser/byte_stream.h" #include "content/browser/download/download_create_info.h" +#include "content/browser/download/download_destination_observer.h" #include "content/browser/download/download_file_factory.h" #include "content/browser/download/download_item_impl_delegate.h" #include "content/browser/download/download_request_handle.h" #include "content/browser/download/mock_download_file.h" -#include "content/public/browser/download_destination_observer.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_url_parameters.h" #include "content/public/common/content_features.h" #include "content/public/test/mock_download_item.h" +#include "content/public/test/mock_download_item.h" #include "content/public/test/test_browser_context.h" -#include "content/public/test/test_browser_thread.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/web_contents_tester.h" +#include "crypto/secure_hash.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using ::testing::_; +using ::testing::DoAll; using ::testing::NiceMock; using ::testing::Property; using ::testing::Return; +using ::testing::ReturnRefOfCopy; using ::testing::SaveArg; using ::testing::StrictMock; +using ::testing::WithArg; +using ::testing::_; const int kDownloadChunkSize = 1000; const int kDownloadSpeed = 1000; -const base::FilePath::CharType kDummyPath[] = FILE_PATH_LITERAL("/testpath"); +const base::FilePath::CharType kDummyTargetPath[] = + FILE_PATH_LITERAL("/testpath"); +const base::FilePath::CharType kDummyIntermediatePath[] = + FILE_PATH_LITERAL("/testpathx"); namespace content { @@ -66,7 +81,6 @@ class MockDelegate : public DownloadItemImplDelegate { void(DownloadUrlParameters* params, uint32_t id)); MOCK_CONST_METHOD0(GetBrowserContext, BrowserContext*()); - MOCK_METHOD1(UpdatePersistence, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadRemoved, void(DownloadItemImpl*)); MOCK_CONST_METHOD1(AssertStateConsistent, void(DownloadItemImpl*)); @@ -128,8 +142,8 @@ class TestDownloadItemObserver : public DownloadItem::Observer { private: void OnDownloadRemoved(DownloadItem* download) override { - DVLOG(20) << " " << __FUNCTION__ - << " download = " << download->DebugString(false); + SCOPED_TRACE(::testing::Message() << " " << __FUNCTION__ << " download = " + << download->DebugString(false)); removed_ = true; } @@ -159,7 +173,7 @@ class TestDownloadItemObserver : public DownloadItem::Observer { << " download = " << download->DebugString(false); destroyed_ = true; item_->RemoveObserver(this); - item_ = NULL; + item_ = nullptr; } DownloadItem* item_; @@ -173,54 +187,82 @@ class TestDownloadItemObserver : public DownloadItem::Observer { // Schedules a task to invoke the RenameCompletionCallback with |new_path| on // the UI thread. Should only be used as the action for -// MockDownloadFile::Rename as follows: -// EXPECT_CALL(download_file, Rename*(_,_)) -// .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, -// new_path)); -ACTION_P2(ScheduleRenameCallback, interrupt_reason, new_path) { +// MockDownloadFile::RenameAndUniquify as follows: +// EXPECT_CALL(download_file, RenameAndUniquify(_,_)) +// .WillOnce(ScheduleRenameAndUniquifyCallback( +// DOWNLOAD_INTERRUPT_REASON_NONE, new_path)); +ACTION_P2(ScheduleRenameAndUniquifyCallback, interrupt_reason, new_path) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(arg1, interrupt_reason, new_path)); } +// Schedules a task to invoke the RenameCompletionCallback with |new_path| on +// the UI thread. Should only be used as the action for +// MockDownloadFile::RenameAndAnnotate as follows: +// EXPECT_CALL(download_file, RenameAndAnnotate(_,_,_,_,_)) +// .WillOnce(ScheduleRenameAndAnnotateCallback( +// DOWNLOAD_INTERRUPT_REASON_NONE, new_path)); +ACTION_P2(ScheduleRenameAndAnnotateCallback, interrupt_reason, new_path) { + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(arg4, interrupt_reason, new_path)); +} + +// Schedules a task to invoke a callback that's bound to the specified +// parameter. +// E.g.: +// +// EXPECT_CALL(foo, Bar(1, _)) +// .WithArg<1>(ScheduleCallbackWithParam(0)); +// +// .. will invoke the second argument to Bar with 0 as the parameter. +ACTION_P(ScheduleCallbackWithParam, param) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(arg0, param)); +} + +// Schedules a task to invoke a closure. +// E.g.: +// +// EXPECT_CALL(foo, Bar(1, _)) +// .WillOnce(ScheduleClosure(some_closure)); +ACTION_P(ScheduleClosure, closure) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, closure); +} + +const char kTestData1[] = {'M', 'a', 'r', 'y', ' ', 'h', 'a', 'd', + ' ', 'a', ' ', 'l', 'i', 't', 't', 'l', + 'e', ' ', 'l', 'a', 'm', 'b', '.'}; + +// SHA256 hash of TestData1 +const uint8_t kHashOfTestData1[] = { + 0xd2, 0xfc, 0x16, 0xa1, 0xf5, 0x1a, 0x65, 0x3a, 0xa0, 0x19, 0x64, + 0xef, 0x9c, 0x92, 0x33, 0x36, 0xe1, 0x06, 0x53, 0xfe, 0xc1, 0x95, + 0xf4, 0x93, 0x45, 0x8b, 0x3b, 0x21, 0x89, 0x0e, 0x1b, 0x97}; + } // namespace class DownloadItemTest : public testing::Test { public: DownloadItemTest() - : ui_thread_(BrowserThread::UI, &loop_), - file_thread_(BrowserThread::FILE, &loop_), - delegate_() { - } - - ~DownloadItemTest() { - } - - virtual void SetUp() { + : delegate_(), next_download_id_(DownloadItem::kInvalidId + 1) { base::FeatureList::ClearInstanceForTesting(); scoped_ptr feature_list(new base::FeatureList); feature_list->InitializeFromCommandLine(features::kDownloadResumption.name, std::string()); base::FeatureList::SetInstance(std::move(feature_list)); - } - virtual void TearDown() { - ui_thread_.DeprecatedGetThreadObject()->message_loop()->RunUntilIdle(); - STLDeleteElements(&allocated_downloads_); + create_info_.reset(new DownloadCreateInfo()); + create_info_->save_info = + scoped_ptr(new DownloadSaveInfo()); + create_info_->save_info->prompt_for_save_location = false; + create_info_->url_chain.push_back(GURL("http://example.com/download")); + create_info_->etag = "SomethingToSatisfyResumption"; } - // This class keeps ownership of the created download item; it will - // be torn down at the end of the test unless DestroyDownloadItem is - // called. - DownloadItemImpl* CreateDownloadItem() { - scoped_ptr info; - - info.reset(new DownloadCreateInfo()); - info->save_info = scoped_ptr(new DownloadSaveInfo()); - info->save_info->prompt_for_save_location = false; - info->url_chain.push_back(GURL()); - info->etag = "SomethingToSatisfyResumption"; - - return CreateDownloadItemWithCreateInfo(std::move(info)); + ~DownloadItemTest() { + RunAllPendingInMessageLoops(); + STLDeleteElements(&allocated_downloads_); } DownloadItemImpl* CreateDownloadItemWithCreateInfo( @@ -231,26 +273,47 @@ class DownloadItemTest : public testing::Test { return download; } - // Add DownloadFile to DownloadItem - MockDownloadFile* AddDownloadFileToDownloadItem( + // This class keeps ownership of the created download item; it will + // be torn down at the end of the test unless DestroyDownloadItem is + // called. + DownloadItemImpl* CreateDownloadItem() { + create_info_->download_id = ++next_download_id_; + DownloadItemImpl* download = + new DownloadItemImpl(&delegate_, create_info_->download_id, + *create_info_, net::BoundNetLog()); + allocated_downloads_.insert(download); + return download; + } + + // Add DownloadFile to DownloadItem. Set |callback| to nullptr if a download + // target determination is not expected. + MockDownloadFile* CallDownloadItemStart( DownloadItemImpl* item, - DownloadItemImplDelegate::DownloadTargetCallback *callback) { - MockDownloadFile* mock_download_file(new StrictMock); - scoped_ptr download_file(mock_download_file); - EXPECT_CALL(*mock_download_file, Initialize(_)); + DownloadItemImplDelegate::DownloadTargetCallback* callback) { + MockDownloadFile* mock_download_file = nullptr; + scoped_ptr download_file; if (callback) { - // Save the callback. EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) .WillOnce(SaveArg<1>(callback)); } else { - // Drop it on the floor. - EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)); + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)).Times(0); + } + + // Only create a DownloadFile if the request was successful. + if (create_info_->result == DOWNLOAD_INTERRUPT_REASON_NONE) { + mock_download_file = new StrictMock; + download_file.reset(mock_download_file); + EXPECT_CALL(*mock_download_file, Initialize(_)) + .WillOnce(ScheduleCallbackWithParam(DOWNLOAD_INTERRUPT_REASON_NONE)); + EXPECT_CALL(*mock_download_file, FullPath()) + .WillRepeatedly(ReturnRefOfCopy(base::FilePath())); } - scoped_ptr request_handle( + scoped_ptr request_handle( new NiceMock); - item->Start(std::move(download_file), std::move(request_handle)); - loop_.RunUntilIdle(); + item->Start(std::move(download_file), std::move(request_handle), + *create_info_); + RunAllPendingInMessageLoops(); // So that we don't have a function writing to a stack variable // lying around if the above failed. @@ -266,27 +329,42 @@ class DownloadItemTest : public testing::Test { } // Perform the intermediate rename for |item|. The target path for the - // download will be set to kDummyPath. Returns the MockDownloadFile* that was - // added to the DownloadItem. + // download will be set to kDummyTargetPath. Returns the MockDownloadFile* + // that was added to the DownloadItem. MockDownloadFile* DoIntermediateRename(DownloadItemImpl* item, DownloadDangerType danger_type) { EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); EXPECT_TRUE(item->GetTargetFilePath().empty()); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); - base::FilePath target_path(kDummyPath); - base::FilePath intermediate_path( - target_path.InsertBeforeExtensionASCII("x")); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); + base::FilePath target_path(kDummyTargetPath); + base::FilePath intermediate_path(kDummyIntermediatePath); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, intermediate_path)); callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type, intermediate_path); RunAllPendingInMessageLoops(); return download_file; } + void DoDestinationComplete(DownloadItemImpl* item, + MockDownloadFile* download_file) { + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) + .WillOnce(Return(true)); + base::FilePath final_path(kDummyTargetPath); + EXPECT_CALL(*download_file, RenameAndAnnotate(_, _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, final_path)); + EXPECT_CALL(*download_file, FullPath()) + .WillRepeatedly(ReturnRefOfCopy(base::FilePath(kDummyTargetPath))); + EXPECT_CALL(*download_file, Detach()); + + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); + RunAllPendingInMessageLoops(); + } + // Cleanup a download item (specifically get rid of the DownloadFile on it). // The item must be in the expected state. void CleanupItem(DownloadItemImpl* item, @@ -298,7 +376,7 @@ class DownloadItemTest : public testing::Test { if (download_file) EXPECT_CALL(*download_file, Cancel()); item->Cancel(true); - loop_.RunUntilIdle(); + RunAllPendingInMessageLoops(); } } @@ -308,9 +386,7 @@ class DownloadItemTest : public testing::Test { delete item; } - void RunAllPendingInMessageLoops() { - loop_.RunUntilIdle(); - } + void RunAllPendingInMessageLoops() { base::RunLoop().RunUntilIdle(); } MockDelegate* mock_delegate() { return &delegate_; @@ -321,13 +397,17 @@ class DownloadItemTest : public testing::Test { *return_path = path; } + DownloadCreateInfo* create_info() { return create_info_.get(); } + + BrowserContext* browser_context() { return &browser_context_; } + private: - int next_download_id_ = DownloadItem::kInvalidId + 1; - base::MessageLoopForUI loop_; - TestBrowserThread ui_thread_; // UI thread - TestBrowserThread file_thread_; // FILE thread StrictMock delegate_; std::set allocated_downloads_; + scoped_ptr create_info_; + uint32_t next_download_id_ = DownloadItem::kInvalidId + 1; + TestBrowserContext browser_context_; + TestBrowserThreadBundle thread_bundle_; }; // Tests to ensure calls that change a DownloadItem generate an update to @@ -340,28 +420,33 @@ class DownloadItemTest : public testing::Test { TEST_F(DownloadItemTest, NotificationAfterUpdate) { DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); + ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); TestDownloadItemObserver observer(item); - item->DestinationUpdate(kDownloadChunkSize, kDownloadSpeed, std::string()); + item->DestinationUpdate(kDownloadChunkSize, kDownloadSpeed); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); EXPECT_EQ(kDownloadSpeed, item->CurrentSpeed()); + CleanupItem(item, file, DownloadItem::IN_PROGRESS); } TEST_F(DownloadItemTest, NotificationAfterCancel) { DownloadItemImpl* user_cancel = CreateDownloadItem(); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(user_cancel, NULL); + CallDownloadItemStart(user_cancel, &target_callback); EXPECT_CALL(*download_file, Cancel()); - TestDownloadItemObserver observer1(user_cancel); + TestDownloadItemObserver observer1(user_cancel); user_cancel->Cancel(true); ASSERT_TRUE(observer1.CheckAndResetDownloadUpdated()); DownloadItemImpl* system_cancel = CreateDownloadItem(); - download_file = AddDownloadFileToDownloadItem(system_cancel, NULL); + download_file = CallDownloadItemStart(system_cancel, &target_callback); EXPECT_CALL(*download_file, Cancel()); - TestDownloadItemObserver observer2(system_cancel); + TestDownloadItemObserver observer2(system_cancel); system_cancel->Cancel(false); ASSERT_TRUE(observer2.CheckAndResetDownloadUpdated()); } @@ -369,11 +454,10 @@ TEST_F(DownloadItemTest, NotificationAfterCancel) { TEST_F(DownloadItemTest, NotificationAfterComplete) { DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); - - item->OnAllDataSaved(DownloadItem::kEmptyFileHash); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); - - item->MarkAsComplete(); + DoDestinationComplete(item, download_file); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); } @@ -396,7 +480,9 @@ TEST_F(DownloadItemTest, NotificationAfterInterrupted) { .Times(0); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + 0, + scoped_ptr()); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); } @@ -408,8 +494,8 @@ TEST_F(DownloadItemTest, NotificationAfterDestroyed) { ASSERT_TRUE(observer.download_destroyed()); } +// Test that a download is resumed automatcially after a continuable interrupt. TEST_F(DownloadItemTest, ContinueAfterInterrupted) { - TestBrowserContext test_browser_context; DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); MockDownloadFile* download_file = @@ -417,13 +503,15 @@ TEST_F(DownloadItemTest, ContinueAfterInterrupted) { // Interrupt the download, using a continuable interrupt. EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)).Times(1); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + 0, + scoped_ptr()); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); // Since the download is resumed automatically, the interrupt count doesn't // increase. @@ -441,7 +529,8 @@ TEST_F(DownloadItemTest, ContinueAfterInterrupted) { CleanupItem(item, nullptr, DownloadItem::IN_PROGRESS); } -// Same as above, but with a non-continuable interrupt. +// Test that automatic resumption doesn't happen after a non-continuable +// interrupt. TEST_F(DownloadItemTest, RestartAfterInterrupted) { DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); @@ -451,7 +540,9 @@ TEST_F(DownloadItemTest, RestartAfterInterrupted) { // Interrupt the download, using a restartable interrupt. EXPECT_CALL(*download_file, Cancel()); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + 0, + scoped_ptr()); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); // Should not try to auto-resume. ASSERT_EQ(1, observer.interrupt_count()); @@ -471,13 +562,15 @@ TEST_F(DownloadItemTest, UnresumableInterrupt) { // Fail final rename with unresumable reason. EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) .WillOnce(Return(true)); - EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, + RenameAndAnnotate(base::FilePath(kDummyTargetPath), _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, base::FilePath())); EXPECT_CALL(*download_file, Cancel()); // Complete download to trigger final rename. - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); RunAllPendingInMessageLoops(); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); @@ -489,25 +582,24 @@ TEST_F(DownloadItemTest, UnresumableInterrupt) { } TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { - TestBrowserContext test_browser_context; DownloadItemImpl* item = CreateDownloadItem(); base::WeakPtr as_observer( item->DestinationObserverAsWeakPtr()); TestDownloadItemObserver observer(item); - MockDownloadFile* mock_download_file(NULL); + MockDownloadFile* mock_download_file(nullptr); scoped_ptr download_file; - MockRequestHandle* mock_request_handle(NULL); + MockRequestHandle* mock_request_handle(nullptr); scoped_ptr request_handle; DownloadItemImplDelegate::DownloadTargetCallback callback; EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) .WillRepeatedly(SaveArg<1>(&callback)); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)) .Times(DownloadItemImpl::kMaxAutoResumeAttempts); for (int i = 0; i < (DownloadItemImpl::kMaxAutoResumeAttempts + 1); ++i) { - DVLOG(20) << "Loop iteration " << i; + SCOPED_TRACE(::testing::Message() << "Iteration " << i); mock_download_file = new NiceMock; download_file.reset(mock_download_file); @@ -515,65 +607,128 @@ TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { request_handle.reset(mock_request_handle); ON_CALL(*mock_download_file, FullPath()) - .WillByDefault(Return(base::FilePath())); + .WillByDefault(ReturnRefOfCopy(base::FilePath())); - // Copied key parts of DoIntermediateRename & AddDownloadFileToDownloadItem + // Copied key parts of DoIntermediateRename & CallDownloadItemStart // to allow for holding onto the request handle. - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); RunAllPendingInMessageLoops(); + + base::FilePath target_path(kDummyTargetPath); + base::FilePath intermediate_path(kDummyIntermediatePath); if (i == 0) { - // Target determination is only done the first time through. - base::FilePath target_path(kDummyPath); - base::FilePath intermediate_path( - target_path.InsertBeforeExtensionASCII("x")); + // RenameAndUniquify is only called the first time. In all the subsequent + // iterations, the intermediate file already has the correct name, hence + // no rename is necessary. EXPECT_CALL(*mock_download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - intermediate_path)); - callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, - DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); - RunAllPendingInMessageLoops(); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, intermediate_path)); } + callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); + RunAllPendingInMessageLoops(); // Use a continuable interrupt. item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + 0, + scoped_ptr()); + RunAllPendingInMessageLoops(); ::testing::Mock::VerifyAndClearExpectations(mock_download_file); } + EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); EXPECT_EQ(1, observer.interrupt_count()); CleanupItem(item, nullptr, DownloadItem::INTERRUPTED); } +// If the download attempts to resume and the resumption request fails, the +// subsequent Start() call shouldn't update the origin state (URL redirect +// chains, Content-Disposition, download URL, etc..) +TEST_F(DownloadItemTest, FailedResumptionDoesntUpdateOriginState) { + const char kContentDisposition[] = "attachment; filename=foo"; + const char kFirstETag[] = "ABC"; + const char kFirstLastModified[] = "Yesterday"; + const char kFirstURL[] = "http://www.example.com/download"; + create_info()->content_disposition = kContentDisposition; + create_info()->etag = kFirstETag; + create_info()->last_modified = kFirstLastModified; + create_info()->url_chain.push_back(GURL(kFirstURL)); + + DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); + EXPECT_EQ(kContentDisposition, item->GetContentDisposition()); + EXPECT_EQ(kFirstETag, item->GetETag()); + EXPECT_EQ(kFirstLastModified, item->GetLastModifiedTime()); + EXPECT_EQ(kFirstURL, item->GetURL().spec()); + + EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)); + EXPECT_CALL(*mock_delegate(), GetBrowserContext()) + .WillRepeatedly(Return(browser_context())); + EXPECT_CALL(*download_file, Detach()); + item->DestinationObserverAsWeakPtr()->DestinationError( + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + 0, + scoped_ptr()); + RunAllPendingInMessageLoops(); + EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + + // Now change the create info. The changes should not cause the DownloadItem + // to be updated. + const char kSecondContentDisposition[] = "attachment; filename=bar"; + const char kSecondETag[] = "123"; + const char kSecondLastModified[] = "Today"; + const char kSecondURL[] = "http://example.com/another-download"; + create_info()->content_disposition = kSecondContentDisposition; + create_info()->etag = kSecondETag; + create_info()->last_modified = kSecondLastModified; + create_info()->url_chain.clear(); + create_info()->url_chain.push_back(GURL(kSecondURL)); + create_info()->result = DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED; + + // The following ends up calling DownloadItem::Start(), but shouldn't result + // in an origin update. + download_file = CallDownloadItemStart(item, nullptr); + + EXPECT_EQ(kContentDisposition, item->GetContentDisposition()); + EXPECT_EQ(kFirstETag, item->GetETag()); + EXPECT_EQ(kFirstLastModified, item->GetLastModifiedTime()); + EXPECT_EQ(kFirstURL, item->GetURL().spec()); + EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item->GetLastReason()); +} + // Test that resumption uses the final URL in a URL chain when resuming. TEST_F(DownloadItemTest, ResumeUsingFinalURL) { - TestBrowserContext test_browser_context; - scoped_ptr create_info(new DownloadCreateInfo); - create_info->save_info = scoped_ptr(new DownloadSaveInfo()); - create_info->save_info->prompt_for_save_location = false; - create_info->etag = "SomethingToSatisfyResumption"; - create_info->url_chain.push_back(GURL("http://example.com/a")); - create_info->url_chain.push_back(GURL("http://example.com/b")); - create_info->url_chain.push_back(GURL("http://example.com/c")); - - DownloadItemImpl* item = - CreateDownloadItemWithCreateInfo(std::move(create_info)); + create_info()->save_info->prompt_for_save_location = false; + create_info()->url_chain.clear(); + create_info()->url_chain.push_back(GURL("http://example.com/a")); + create_info()->url_chain.push_back(GURL("http://example.com/b")); + create_info()->url_chain.push_back(GURL("http://example.com/c")); + + DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); MockDownloadFile* download_file = DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); // Interrupt the download, using a continuable interrupt. - EXPECT_CALL(*download_file, FullPath()).WillOnce(Return(base::FilePath())); + EXPECT_CALL(*download_file, FullPath()) + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload( Property(&DownloadUrlParameters::url, GURL("http://example.com/c")), _)) .Times(1); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + 0, + scoped_ptr()); // Test expectations verify that ResumeInterruptedDownload() is called (by way // of MockResumeInterruptedDownload) after the download is interrupted. But @@ -588,7 +743,9 @@ TEST_F(DownloadItemTest, ResumeUsingFinalURL) { TEST_F(DownloadItemTest, NotificationAfterRemove) { DownloadItemImpl* item = CreateDownloadItem(); - MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + MockDownloadFile* download_file = + CallDownloadItemStart(item, &target_callback); EXPECT_CALL(*download_file, Cancel()); EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); TestDownloadItemObserver observer(item); @@ -601,37 +758,52 @@ TEST_F(DownloadItemTest, NotificationAfterRemove) { TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { // Setting to NOT_DANGEROUS does not trigger a notification. DownloadItemImpl* safe_item = CreateDownloadItem(); + MockDownloadFile* download_file = + DoIntermediateRename(safe_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); TestDownloadItemObserver safe_observer(safe_item); - safe_item->OnAllDataSaved(std::string()); + safe_item->OnAllDataSaved(0, scoped_ptr()); EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated()); safe_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated()); + CleanupItem(safe_item, download_file, DownloadItem::IN_PROGRESS); // Setting to unsafe url or unsafe file should trigger a notification. DownloadItemImpl* unsafeurl_item = CreateDownloadItem(); + download_file = + DoIntermediateRename(unsafeurl_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); TestDownloadItemObserver unsafeurl_observer(unsafeurl_item); - unsafeurl_item->OnAllDataSaved(std::string()); + unsafeurl_item->OnAllDataSaved(0, scoped_ptr()); EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated()); unsafeurl_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_URL); EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated()); + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*download_file, RenameAndAnnotate(_, _, _, _, _)); unsafeurl_item->ValidateDangerousDownload(); EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated()); + CleanupItem(unsafeurl_item, download_file, DownloadItem::IN_PROGRESS); DownloadItemImpl* unsafefile_item = CreateDownloadItem(); + download_file = + DoIntermediateRename(unsafefile_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); TestDownloadItemObserver unsafefile_observer(unsafefile_item); - unsafefile_item->OnAllDataSaved(std::string()); + unsafefile_item->OnAllDataSaved(0, scoped_ptr()); EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated()); unsafefile_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated()); + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*download_file, RenameAndAnnotate(_, _, _, _, _)); unsafefile_item->ValidateDangerousDownload(); EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated()); + CleanupItem(unsafefile_item, download_file, DownloadItem::IN_PROGRESS); } // DownloadItemImpl::OnDownloadTargetDetermined will schedule a task to run @@ -642,16 +814,15 @@ TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); TestDownloadItemObserver observer(item); - base::FilePath target_path(kDummyPath); + base::FilePath target_path(kDummyTargetPath); base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( target_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, new_intermediate_path)); // Currently, a notification would be generated if the danger type is anything // other than NOT_DANGEROUS. @@ -675,7 +846,8 @@ TEST_F(DownloadItemTest, NotificationAfterTogglePause) { EXPECT_CALL(*mock_download_file, Initialize(_)); EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)); - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); item->Pause(); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); @@ -693,15 +865,15 @@ TEST_F(DownloadItemTest, NotificationAfterTogglePause) { TEST_F(DownloadItemTest, DisplayName) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); - base::FilePath target_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); + base::FilePath target_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x")); EXPECT_EQ(FILE_PATH_LITERAL(""), item->GetFileNameToReportUser().value()); EXPECT_CALL(*download_file, RenameAndUniquify(_, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, intermediate_path)); callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); RunAllPendingInMessageLoops(); @@ -722,25 +894,90 @@ TEST_F(DownloadItemTest, Start) { scoped_ptr request_handle( new NiceMock); EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)); - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); RunAllPendingInMessageLoops(); CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS); } +// Download file and the request should be cancelled as a result of download +// file initialization failing. +TEST_F(DownloadItemTest, InitDownloadFileFails) { + DownloadItemImpl* item = CreateDownloadItem(); + scoped_ptr file(new MockDownloadFile()); + scoped_ptr request_handle(new MockRequestHandle()); + EXPECT_CALL(*file, Cancel()); + EXPECT_CALL(*request_handle, CancelRequest()); + EXPECT_CALL(*file, Initialize(_)) + .WillOnce(ScheduleCallbackWithParam( + DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED)); + + base::RunLoop start_download_loop; + DownloadItemImplDelegate::DownloadTargetCallback download_target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) + .WillOnce(DoAll(SaveArg<1>(&download_target_callback), + ScheduleClosure(start_download_loop.QuitClosure()))); + + item->Start(std::move(file), std::move(request_handle), *create_info()); + start_download_loop.Run(); + + download_target_callback.Run(base::FilePath(kDummyTargetPath), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + base::FilePath(kDummyIntermediatePath)); + RunAllPendingInMessageLoops(); + + EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, + item->GetLastReason()); +} + +// Handling of downloads initiated via a failed request. In this case, Start() +// will get called with a DownloadCreateInfo with a non-zero interrupt_reason. +TEST_F(DownloadItemTest, StartFailedDownload) { + create_info()->result = DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED; + DownloadItemImpl* item = CreateDownloadItem(); + + // DownloadFile and DownloadRequestHandleInterface objects aren't created for + // failed downloads. + scoped_ptr null_download_file; + scoped_ptr null_request_handle; + DownloadItemImplDelegate::DownloadTargetCallback download_target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) + .WillOnce(SaveArg<1>(&download_target_callback)); + item->Start(std::move(null_download_file), std::move(null_request_handle), + *create_info()); + EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + RunAllPendingInMessageLoops(); + + // The DownloadItemImpl should attempt to determine a target path even if the + // download was interrupted. + ASSERT_FALSE(download_target_callback.is_null()); + ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + base::FilePath target_path(FILE_PATH_LITERAL("foo")); + download_target_callback.Run(target_path, + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, target_path); + RunAllPendingInMessageLoops(); + + EXPECT_EQ(target_path, item->GetTargetFilePath()); + CleanupItem(item, NULL, DownloadItem::INTERRUPTED); +} + // Test that the delegate is invoked after the download file is renamed. TEST_F(DownloadItemTest, CallbackAfterRename) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); - base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); + base::FilePath final_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( final_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, new_intermediate_path)); callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); @@ -751,13 +988,14 @@ TEST_F(DownloadItemTest, CallbackAfterRename) { EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) .WillOnce(Return(true)); - EXPECT_CALL(*download_file, RenameAndAnnotate(final_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - final_path)); + EXPECT_CALL(*download_file, RenameAndAnnotate(final_path, _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, final_path)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); RunAllPendingInMessageLoops(); ::testing::Mock::VerifyAndClearExpectations(download_file); mock_delegate()->VerifyAndClearExpectations(); @@ -768,15 +1006,15 @@ TEST_F(DownloadItemTest, CallbackAfterRename) { TEST_F(DownloadItemTest, CallbackAfterInterruptedRename) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); - base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); + base::FilePath final_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( final_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, new_intermediate_path)); EXPECT_CALL(*download_file, Cancel()) .Times(1); @@ -798,7 +1036,8 @@ TEST_F(DownloadItemTest, Interrupted) { // Confirm interrupt sets state properly. EXPECT_CALL(*download_file, Cancel()); - item->DestinationObserverAsWeakPtr()->DestinationError(reason); + item->DestinationObserverAsWeakPtr()->DestinationError( + reason, 0, scoped_ptr()); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); EXPECT_EQ(reason, item->GetLastReason()); @@ -814,19 +1053,21 @@ TEST_F(DownloadItemTest, Interrupted) { TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + 0, + scoped_ptr()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); - base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + base::FilePath final_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( final_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, new_intermediate_path)); EXPECT_CALL(*download_file, Cancel()) .Times(1); @@ -847,21 +1088,23 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) { TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + 0, + scoped_ptr()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); - base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + base::FilePath final_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( final_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, new_intermediate_path)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath(new_intermediate_path))); + .WillOnce(ReturnRefOfCopy(base::FilePath(new_intermediate_path))); EXPECT_CALL(*download_file, Detach()); callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, @@ -876,23 +1119,25 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { } // As above. If the intermediate rename fails, then the interrupt reason should -// be set to the destination error and the intermediate path should be empty. +// be set to the file error and the intermediate path should be empty. TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) { DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; - MockDownloadFile* download_file = - AddDownloadFileToDownloadItem(item, &callback); + MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + 0, + scoped_ptr()); ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); - base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); + base::FilePath final_path( + base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); base::FilePath new_intermediate_path( final_path.InsertBeforeExtensionASCII("y")); EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - new_intermediate_path)); + .WillOnce(ScheduleRenameAndUniquifyCallback( + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, new_intermediate_path)); EXPECT_CALL(*download_file, Cancel()) .Times(1); @@ -903,14 +1148,16 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) { ::testing::Mock::VerifyAndClearExpectations(download_file); mock_delegate()->VerifyAndClearExpectations(); EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); - EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item->GetLastReason()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, item->GetLastReason()); EXPECT_TRUE(item->GetFullPath().empty()); EXPECT_EQ(final_path, item->GetTargetFilePath()); } TEST_F(DownloadItemTest, Canceled) { DownloadItemImpl* item = CreateDownloadItem(); - MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + MockDownloadFile* download_file = + CallDownloadItemStart(item, &target_callback); // Confirm cancel sets state properly. EXPECT_CALL(*download_file, Cancel()); @@ -928,34 +1175,62 @@ TEST_F(DownloadItemTest, FileRemoved) { TEST_F(DownloadItemTest, DestinationUpdate) { DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); base::WeakPtr as_observer( item->DestinationObserverAsWeakPtr()); TestDownloadItemObserver observer(item); EXPECT_EQ(0l, item->CurrentSpeed()); - EXPECT_EQ("", item->GetHashState()); EXPECT_EQ(0l, item->GetReceivedBytes()); EXPECT_EQ(0l, item->GetTotalBytes()); EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); item->SetTotalBytes(100l); EXPECT_EQ(100l, item->GetTotalBytes()); - as_observer->DestinationUpdate(10, 20, "deadbeef"); + as_observer->DestinationUpdate(10, 20); EXPECT_EQ(20l, item->CurrentSpeed()); - EXPECT_EQ("deadbeef", item->GetHashState()); EXPECT_EQ(10l, item->GetReceivedBytes()); EXPECT_EQ(100l, item->GetTotalBytes()); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); - as_observer->DestinationUpdate(200, 20, "livebeef"); + as_observer->DestinationUpdate(200, 20); EXPECT_EQ(20l, item->CurrentSpeed()); - EXPECT_EQ("livebeef", item->GetHashState()); EXPECT_EQ(200l, item->GetReceivedBytes()); EXPECT_EQ(0l, item->GetTotalBytes()); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); + + CleanupItem(item, file, DownloadItem::IN_PROGRESS); } -TEST_F(DownloadItemTest, DestinationError) { +TEST_F(DownloadItemTest, DestinationError_NoRestartRequired) { + DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); + base::WeakPtr as_observer( + item->DestinationObserverAsWeakPtr()); + TestDownloadItemObserver observer(item); + + EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, item->GetLastReason()); + EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); + + scoped_ptr hash( + crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + hash->Update(kTestData1, sizeof(kTestData1)); + + EXPECT_CALL(*download_file, Detach()); + as_observer->DestinationError( + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, 0, std::move(hash)); + mock_delegate()->VerifyAndClearExpectations(); + EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); + EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item->GetLastReason()); + EXPECT_EQ( + std::string(std::begin(kHashOfTestData1), std::end(kHashOfTestData1)), + item->GetHash()); +} +TEST_F(DownloadItemTest, DestinationError_RestartRequired) { DownloadItemImpl* item = CreateDownloadItem(); MockDownloadFile* download_file = DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); @@ -967,43 +1242,58 @@ TEST_F(DownloadItemTest, DestinationError) { EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, item->GetLastReason()); EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); + scoped_ptr hash( + crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + hash->Update(kTestData1, sizeof(kTestData1)); + EXPECT_CALL(*download_file, Cancel()); as_observer->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, 0, std::move(hash)); mock_delegate()->VerifyAndClearExpectations(); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); - EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, - item->GetLastReason()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, item->GetLastReason()); + EXPECT_EQ(std::string(), item->GetHash()); } TEST_F(DownloadItemTest, DestinationCompleted) { DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); base::WeakPtr as_observer( item->DestinationObserverAsWeakPtr()); TestDownloadItemObserver observer(item); EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); EXPECT_EQ("", item->GetHash()); - EXPECT_EQ("", item->GetHashState()); EXPECT_FALSE(item->AllDataSaved()); EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); - as_observer->DestinationUpdate(10, 20, "deadbeef"); + as_observer->DestinationUpdate(10, 20); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); // Confirm reset. EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); EXPECT_EQ("", item->GetHash()); - EXPECT_EQ("deadbeef", item->GetHashState()); EXPECT_FALSE(item->AllDataSaved()); - as_observer->DestinationCompleted("livebeef"); + scoped_ptr hash( + crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + hash->Update(kTestData1, sizeof(kTestData1)); + + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)); + as_observer->DestinationCompleted(10, std::move(hash)); mock_delegate()->VerifyAndClearExpectations(); EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); EXPECT_TRUE(observer.CheckAndResetDownloadUpdated()); - EXPECT_EQ("livebeef", item->GetHash()); - EXPECT_EQ("", item->GetHashState()); + EXPECT_EQ( + std::string(std::begin(kHashOfTestData1), std::end(kHashOfTestData1)), + item->GetHash()); EXPECT_TRUE(item->AllDataSaved()); + + // Even though the DownloadItem receives a DestinationCompleted() event, + // target determination hasn't completed, hence the download item is stuck in + // TARGET_PENDING. + CleanupItem(item, download_file, DownloadItem::IN_PROGRESS); } TEST_F(DownloadItemTest, EnabledActionsForNormalDownload) { @@ -1018,15 +1308,16 @@ TEST_F(DownloadItemTest, EnabledActionsForNormalDownload) { EXPECT_TRUE(item->CanOpenDownload()); // Complete - EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, RenameAndAnnotate(_, _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) .WillOnce(Return(true)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); RunAllPendingInMessageLoops(); ASSERT_EQ(DownloadItem::COMPLETE, item->GetState()); @@ -1050,13 +1341,14 @@ TEST_F(DownloadItemTest, EnabledActionsForTemporaryDownload) { // Complete Temporary EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) .WillOnce(Return(true)); - EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, RenameAndAnnotate(_, _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); RunAllPendingInMessageLoops(); ASSERT_EQ(DownloadItem::COMPLETE, item->GetState()); @@ -1071,13 +1363,15 @@ TEST_F(DownloadItemTest, EnabledActionsForInterruptedDownload) { EXPECT_CALL(*download_file, Cancel()); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + 0, + scoped_ptr()); RunAllPendingInMessageLoops(); ASSERT_EQ(DownloadItem::INTERRUPTED, item->GetState()); ASSERT_FALSE(item->GetTargetFilePath().empty()); EXPECT_FALSE(item->CanShowInFolder()); - EXPECT_FALSE(item->CanOpenDownload()); + EXPECT_TRUE(item->CanOpenDownload()); } TEST_F(DownloadItemTest, EnabledActionsForCancelledDownload) { @@ -1107,18 +1401,20 @@ TEST_F(DownloadItemTest, CompleteDelegate_ReturnTrue) { // Drive the delegate interaction. EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) .WillOnce(Return(true)); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); EXPECT_FALSE(item->IsDangerous()); // Make sure the download can complete. - EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, + RenameAndAnnotate(base::FilePath(kDummyTargetPath), _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) .WillOnce(Return(true)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::COMPLETE, item->GetState()); @@ -1139,7 +1435,8 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockOnce) { .WillOnce(DoAll(SaveArg<1>(&delegate_callback), Return(false))) .WillOnce(Return(true)); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); ASSERT_FALSE(delegate_callback.is_null()); copy_delegate_callback = delegate_callback; delegate_callback.Reset(); @@ -1150,13 +1447,14 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockOnce) { EXPECT_FALSE(item->IsDangerous()); // Make sure the download can complete. - EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, + RenameAndAnnotate(base::FilePath(kDummyTargetPath), _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) .WillOnce(Return(true)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::COMPLETE, item->GetState()); @@ -1177,7 +1475,8 @@ TEST_F(DownloadItemTest, CompleteDelegate_SetDanger) { .WillOnce(DoAll(SaveArg<1>(&delegate_callback), Return(false))) .WillOnce(Return(true)); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); ASSERT_FALSE(delegate_callback.is_null()); copy_delegate_callback = delegate_callback; delegate_callback.Reset(); @@ -1191,13 +1490,14 @@ TEST_F(DownloadItemTest, CompleteDelegate_SetDanger) { EXPECT_TRUE(item->IsDangerous()); // Make sure the download doesn't complete until we've validated it. - EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, + RenameAndAnnotate(base::FilePath(kDummyTargetPath), _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) .WillOnce(Return(true)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); @@ -1226,7 +1526,8 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockTwice) { .WillOnce(DoAll(SaveArg<1>(&delegate_callback), Return(false))) .WillOnce(Return(true)); - item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string()); + item->DestinationObserverAsWeakPtr()->DestinationCompleted( + 0, scoped_ptr()); ASSERT_FALSE(delegate_callback.is_null()); copy_delegate_callback = delegate_callback; delegate_callback.Reset(); @@ -1242,13 +1543,14 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockTwice) { EXPECT_FALSE(item->IsDangerous()); // Make sure the download can complete. - EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) - .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, - base::FilePath(kDummyPath))); + EXPECT_CALL(*download_file, + RenameAndAnnotate(base::FilePath(kDummyTargetPath), _, _, _, _)) + .WillOnce(ScheduleRenameAndAnnotateCallback( + DOWNLOAD_INTERRUPT_REASON_NONE, base::FilePath(kDummyTargetPath))); EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) .WillOnce(Return(true)); EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(base::FilePath())); + .WillOnce(ReturnRefOfCopy(base::FilePath())); EXPECT_CALL(*download_file, Detach()); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::COMPLETE, item->GetState()); @@ -1262,8 +1564,7 @@ TEST_F(DownloadItemTest, StealDangerousDownload) { base::FilePath full_path(FILE_PATH_LITERAL("foo.txt")); base::FilePath returned_path; - EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(full_path)); + EXPECT_CALL(*download_file, FullPath()).WillOnce(ReturnRefOfCopy(full_path)); EXPECT_CALL(*download_file, Detach()); EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); base::WeakPtrFactory weak_ptr_factory(this); @@ -1282,11 +1583,12 @@ TEST_F(DownloadItemTest, StealInterruptedDangerousDownload) { DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); base::FilePath full_path = item->GetFullPath(); EXPECT_FALSE(full_path.empty()); - EXPECT_CALL(*download_file, FullPath()) - .WillOnce(Return(full_path)); + EXPECT_CALL(*download_file, FullPath()).WillOnce(ReturnRefOfCopy(full_path)); EXPECT_CALL(*download_file, Detach()); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); + DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + 0, + scoped_ptr()); ASSERT_TRUE(item->IsDangerous()); EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); @@ -1306,7 +1608,9 @@ TEST_F(DownloadItemTest, StealInterruptedNonResumableDangerousDownload) { DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); EXPECT_CALL(*download_file, Cancel()); item->DestinationObserverAsWeakPtr()->DestinationError( - DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + 0, + scoped_ptr()); ASSERT_TRUE(item->IsDangerous()); EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); @@ -1319,6 +1623,428 @@ TEST_F(DownloadItemTest, StealInterruptedNonResumableDangerousDownload) { EXPECT_TRUE(returned_path.empty()); } +namespace { + +// The DownloadItemDestinationUpdateRaceTest fixture (defined below) is used to +// test for race conditions between download destination events received via the +// DownloadDestinationObserver interface, and the target determination logic. +// +// The general control flow for DownloadItemImpl looks like this: +// +// * Start() called, which in turn calls DownloadFile::Initialize(). +// +// Even though OnDownloadFileInitialized hasn't been called, there could now +// be destination observer calls queued prior to the task that calls +// OnDownloadFileInitialized. Let's call this point in the workflow "A". +// +// * DownloadItemImpl::OnDownloadFileInitialized() called. +// +// * Assuming the result is successful, DII now invokes the delegate's +// DetermineDownloadTarget method. +// +// At this point DonwnloadFile acts as the source of +// DownloadDestinationObserver events, and may invoke callbacks. Let's call +// this point in the workflow "B". +// +// * DII::OnDownloadTargetDetermined() invoked after delegate is done with +// target determination. +// +// * DII attempts to rename the DownloadFile to its intermediate name. +// +// More DownloadDestinationObserver events can happen here. Let's call this +// point in the workflow "C". +// +// * DII::OnDownloadRenamedToIntermediateName() invoked. Assuming all went well, +// DII is now in IN_PROGRESS state. +// +// More DownloadDestinationObserver events can happen here. Let's call this +// point in the workflow "D". +// +// The DownloadItemDestinationUpdateRaceTest works by generating various +// combinations of DownloadDestinationObserver events that might occur at the +// points "A", "B", "C", and "D" above. Each test in this suite cranks a +// DownloadItemImpl through the states listed above and invokes the events +// assigned to each position. + +// This type of callback represents a call to a DownloadDestinationObserver +// method that's missing the DownloadDestinationObserver object. Currying this +// way allows us to bind a call prior to constructing the object on which the +// method would be invoked. This is necessary since we are going to construct +// various permutations of observer calls that will then be applied to a +// DownloadItem in a state as yet undetermined. +using CurriedObservation = + base::Callback)>; + +// A list of observations that are to be made during some event in the +// DownloadItemImpl control flow. Ordering of the observations is significant. +using ObservationList = std::deque; + +// An ordered list of events. +// +// An "event" in this context refers to some stage in the DownloadItemImpl's +// workflow described as "A", "B", "C", or "D" above. An EventList is expected +// to always contains kEventCount events. +using EventList = std::deque; + +// Number of events in an EventList. This is always 4 for now as described +// above. +const int kEventCount = 4; + +// The following functions help us with currying the calls to +// DownloadDestinationObserver. If std::bind was allowed along with +// std::placeholders, it is possible to avoid these functions, but currently +// Chromium doesn't allow using std::bind for good reasons. +void DestinationUpdateInvoker( + int64_t bytes_so_far, + int64_t bytes_per_sec, + base::WeakPtr observer) { + DVLOG(20) << "DestinationUpdate(bytes_so_far:" << bytes_so_far + << ", bytes_per_sec:" << bytes_per_sec + << ") observer:" << !!observer; + if (observer) + observer->DestinationUpdate(bytes_so_far, bytes_per_sec); +} + +void DestinationErrorInvoker( + DownloadInterruptReason reason, + int64_t bytes_so_far, + base::WeakPtr observer) { + DVLOG(20) << "DestinationError(reason:" + << DownloadInterruptReasonToString(reason) + << ", bytes_so_far:" << bytes_so_far << ") observer:" << !!observer; + if (observer) + observer->DestinationError( + reason, bytes_so_far, scoped_ptr()); +} + +void DestinationCompletedInvoker( + int64_t total_bytes, + base::WeakPtr observer) { + DVLOG(20) << "DestinationComplete(total_bytes:" << total_bytes + << ") observer:" << !!observer; + if (observer) + observer->DestinationCompleted(total_bytes, + scoped_ptr()); +} + +// Given a set of observations (via the range |begin|..|end|), constructs a list +// of EventLists such that: +// +// * There are exactly |event_count| ObservationSets in each EventList. +// +// * Each ObservationList in each EventList contains a subrange (possibly empty) +// of observations from the input range, in the same order as the input range. +// +// * The ordering of the ObservationList in each EventList is such that all +// observations in one ObservationList occur earlier than all observations in +// an ObservationList that follows it. +// +// * The list of EventLists together describe all the possible ways in which the +// list of observations can be distributed into |event_count| events. +std::vector DistributeObservationsIntoEvents( + const std::vector::iterator begin, + const std::vector::iterator end, + int event_count) { + std::vector all_event_lists; + for (auto partition = begin;; ++partition) { + ObservationList first_group_of_observations(begin, partition); + if (event_count > 1) { + std::vector list_of_subsequent_events = + DistributeObservationsIntoEvents(partition, end, event_count - 1); + for (const auto& subsequent_events : list_of_subsequent_events) { + EventList event_list; + event_list = subsequent_events; + event_list.push_front(first_group_of_observations); + all_event_lists.push_back(event_list); + } + } else { + EventList event_list; + event_list.push_front(first_group_of_observations); + all_event_lists.push_back(event_list); + } + if (partition == end) + break; + } + return all_event_lists; +} + +// For the purpose of this tests, we are only concerned with 3 events: +// +// 1. Immediately after the DownloadFile is initialized. +// 2. Immediately after the DownloadTargetCallback is invoked. +// 3. Immediately after the intermediate file is renamed. +// +// We are going to take a couple of sets of DownloadDestinationObserver events +// and distribute them into the three events described above. And then we are +// going to invoke the observations while a DownloadItemImpl is carefully +// stepped through its stages. + +std::vector GenerateSuccessfulEventLists() { + std::vector all_observations; + all_observations.push_back(base::Bind(&DestinationUpdateInvoker, 100, 100)); + all_observations.push_back(base::Bind(&DestinationUpdateInvoker, 200, 100)); + all_observations.push_back(base::Bind(&DestinationCompletedInvoker, 200)); + return DistributeObservationsIntoEvents(all_observations.begin(), + all_observations.end(), kEventCount); +} + +std::vector GenerateFailingEventLists() { + std::vector all_observations; + all_observations.push_back(base::Bind(&DestinationUpdateInvoker, 100, 100)); + all_observations.push_back(base::Bind( + &DestinationErrorInvoker, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, 100)); + return DistributeObservationsIntoEvents(all_observations.begin(), + all_observations.end(), kEventCount); +} + +class DownloadItemDestinationUpdateRaceTest + : public DownloadItemTest, + public ::testing::WithParamInterface { + public: + DownloadItemDestinationUpdateRaceTest() + : DownloadItemTest(), + item_(CreateDownloadItem()), + file_(new ::testing::StrictMock()), + request_handle_(new ::testing::StrictMock()) { + DCHECK_EQ(GetParam().size(), static_cast(kEventCount)); + EXPECT_CALL(*request_handle_, GetWebContents()) + .WillRepeatedly(Return(nullptr)); + } + + protected: + const ObservationList& PreInitializeFileObservations() { + return GetParam().front(); + } + const ObservationList& PostInitializeFileObservations() { + return *(GetParam().begin() + 1); + } + const ObservationList& PostTargetDeterminationObservations() { + return *(GetParam().begin() + 2); + } + const ObservationList& PostIntermediateRenameObservations() { + return *(GetParam().begin() + 3); + } + + // Apply all the observations in |observations| to |observer|, but do so + // asynchronously so that the events are applied in order behind any tasks + // that are already scheduled. + void ScheduleObservations( + const ObservationList& observations, + base::WeakPtr observer) { + for (const auto action : observations) + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(action, observer)); + } + + DownloadItemImpl* item_; + scoped_ptr file_; + scoped_ptr request_handle_; + + std::queue successful_update_events_; + std::queue failing_update_events_; +}; + +INSTANTIATE_TEST_CASE_P(Success, + DownloadItemDestinationUpdateRaceTest, + ::testing::ValuesIn(GenerateSuccessfulEventLists())); + +INSTANTIATE_TEST_CASE_P(Failure, + DownloadItemDestinationUpdateRaceTest, + ::testing::ValuesIn(GenerateFailingEventLists())); + +} // namespace + +// Run through the DII workflow but the embedder cancels the download at target +// determination. +TEST_P(DownloadItemDestinationUpdateRaceTest, DownloadCancelledByUser) { + // Expect that the download file and the request will be cancelled as a + // result. + EXPECT_CALL(*file_, Cancel()); + EXPECT_CALL(*request_handle_, CancelRequest()); + + base::RunLoop download_start_loop; + DownloadFile::InitializeCallback initialize_callback; + EXPECT_CALL(*file_, Initialize(_)) + .WillOnce(DoAll(SaveArg<0>(&initialize_callback), + ScheduleClosure(download_start_loop.QuitClosure()))); + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); + download_start_loop.Run(); + + base::WeakPtr destination_observer = + item_->DestinationObserverAsWeakPtr(); + + ScheduleObservations(PreInitializeFileObservations(), destination_observer); + RunAllPendingInMessageLoops(); + + base::RunLoop initialize_completion_loop; + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) + .WillOnce( + DoAll(SaveArg<1>(&target_callback), + ScheduleClosure(initialize_completion_loop.QuitClosure()))); + ScheduleObservations(PostInitializeFileObservations(), destination_observer); + initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); + initialize_completion_loop.Run(); + + RunAllPendingInMessageLoops(); + + ASSERT_FALSE(target_callback.is_null()); + ScheduleObservations(PostTargetDeterminationObservations(), + destination_observer); + target_callback.Run(base::FilePath(), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, base::FilePath()); + EXPECT_EQ(DownloadItem::CANCELLED, item_->GetState()); + RunAllPendingInMessageLoops(); +} + +// Run through the DII workflow, but the intermediate rename fails. +TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameFails) { + // Expect that the download file and the request will be cancelled as a + // result. + EXPECT_CALL(*file_, Cancel()); + EXPECT_CALL(*request_handle_, CancelRequest()); + + // Intermediate rename loop is not used immediately, but let's set up the + // DownloadFile expectations since we are about to transfer its ownership to + // the DownloadItem. + base::RunLoop intermediate_rename_loop; + DownloadFile::RenameCompletionCallback intermediate_rename_callback; + EXPECT_CALL(*file_, RenameAndUniquify(_, _)) + .WillOnce(DoAll(SaveArg<1>(&intermediate_rename_callback), + ScheduleClosure(intermediate_rename_loop.QuitClosure()))); + + base::RunLoop download_start_loop; + DownloadFile::InitializeCallback initialize_callback; + EXPECT_CALL(*file_, Initialize(_)) + .WillOnce(DoAll(SaveArg<0>(&initialize_callback), + ScheduleClosure(download_start_loop.QuitClosure()))); + + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); + download_start_loop.Run(); + base::WeakPtr destination_observer = + item_->DestinationObserverAsWeakPtr(); + + ScheduleObservations(PreInitializeFileObservations(), destination_observer); + RunAllPendingInMessageLoops(); + + base::RunLoop initialize_completion_loop; + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) + .WillOnce( + DoAll(SaveArg<1>(&target_callback), + ScheduleClosure(initialize_completion_loop.QuitClosure()))); + ScheduleObservations(PostInitializeFileObservations(), destination_observer); + initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); + initialize_completion_loop.Run(); + + RunAllPendingInMessageLoops(); + ASSERT_FALSE(target_callback.is_null()); + + ScheduleObservations(PostTargetDeterminationObservations(), + destination_observer); + target_callback.Run(base::FilePath(kDummyTargetPath), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + base::FilePath(kDummyIntermediatePath)); + + intermediate_rename_loop.Run(); + ASSERT_FALSE(intermediate_rename_callback.is_null()); + + ScheduleObservations(PostIntermediateRenameObservations(), + destination_observer); + intermediate_rename_callback.Run(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + base::FilePath()); + RunAllPendingInMessageLoops(); + + EXPECT_EQ(DownloadItem::INTERRUPTED, item_->GetState()); +} + +// Run through the DII workflow. Download file initialization, target +// determination and intermediate rename all succeed. +TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameSucceeds) { + // We expect either that the download will fail (in which case the request and + // the download file will be cancelled), or it will succeed (in which case the + // DownloadFile will Detach()). It depends on the list of observations that + // are given to us. + EXPECT_CALL(*file_, Cancel()).Times(::testing::AnyNumber()); + EXPECT_CALL(*request_handle_, CancelRequest()).Times(::testing::AnyNumber()); + EXPECT_CALL(*file_, Detach()).Times(::testing::AnyNumber()); + + EXPECT_CALL(*file_, FullPath()) + .WillRepeatedly(ReturnRefOfCopy(base::FilePath(kDummyIntermediatePath))); + + // Intermediate rename loop is not used immediately, but let's set up the + // DownloadFile expectations since we are about to transfer its ownership to + // the DownloadItem. + base::RunLoop intermediate_rename_loop; + DownloadFile::RenameCompletionCallback intermediate_rename_callback; + EXPECT_CALL(*file_, RenameAndUniquify(_, _)) + .WillOnce(DoAll(SaveArg<1>(&intermediate_rename_callback), + ScheduleClosure(intermediate_rename_loop.QuitClosure()))); + + base::RunLoop download_start_loop; + DownloadFile::InitializeCallback initialize_callback; + EXPECT_CALL(*file_, Initialize(_)) + .WillOnce(DoAll(SaveArg<0>(&initialize_callback), + ScheduleClosure(download_start_loop.QuitClosure()))); + + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); + download_start_loop.Run(); + base::WeakPtr destination_observer = + item_->DestinationObserverAsWeakPtr(); + + ScheduleObservations(PreInitializeFileObservations(), destination_observer); + RunAllPendingInMessageLoops(); + + base::RunLoop initialize_completion_loop; + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) + .WillOnce( + DoAll(SaveArg<1>(&target_callback), + ScheduleClosure(initialize_completion_loop.QuitClosure()))); + ScheduleObservations(PostInitializeFileObservations(), destination_observer); + initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); + initialize_completion_loop.Run(); + + RunAllPendingInMessageLoops(); + ASSERT_FALSE(target_callback.is_null()); + + ScheduleObservations(PostTargetDeterminationObservations(), + destination_observer); + target_callback.Run(base::FilePath(kDummyTargetPath), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + base::FilePath(kDummyIntermediatePath)); + + intermediate_rename_loop.Run(); + ASSERT_FALSE(intermediate_rename_callback.is_null()); + + // This may or may not be called, depending on whether there are any errors in + // our action list. + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) + .Times(::testing::AnyNumber()); + + ScheduleObservations(PostIntermediateRenameObservations(), + destination_observer); + intermediate_rename_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE, + base::FilePath(kDummyIntermediatePath)); + RunAllPendingInMessageLoops(); + + // The state of the download depends on the observer events that were played + // back to the DownloadItemImpl. Hence we can't establish a single expectation + // here. On Debug builds, the DCHECKs will verify that the state transitions + // were correct. On Release builds, tests are expected to run to completion + // without crashing on success. + EXPECT_TRUE(item_->GetState() == DownloadItem::IN_PROGRESS || + item_->GetState() == DownloadItem::INTERRUPTED); + if (item_->GetState() == DownloadItem::INTERRUPTED) + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item_->GetLastReason()); + + item_->Cancel(true); + RunAllPendingInMessageLoops(); +} + TEST(MockDownloadItem, Compiles) { MockDownloadItem mock_item; } diff --git a/chromium/content/browser/download/download_manager_impl.cc b/chromium/content/browser/download/download_manager_impl.cc index 97c0a85e714..28f1c3fca65 100644 --- a/chromium/content/browser/download/download_manager_impl.cc +++ b/chromium/content/browser/download/download_manager_impl.cc @@ -45,6 +45,7 @@ #include "net/base/request_priority.h" #include "net/base/upload_bytes_element_reader.h" #include "net/url_request/url_request_context.h" +#include "storage/browser/blob/blob_url_request_job_factory.h" #include "url/origin.h" namespace content { @@ -55,90 +56,53 @@ scoped_ptr BeginDownload( uint32_t download_id, base::WeakPtr download_manager) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and - // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so - // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4. - scoped_ptr request( - params->resource_context()->GetRequestContext()->CreateRequest( - params->url(), net::DEFAULT_PRIORITY, NULL)); - request->set_method(params->method()); - if (!params->post_body().empty()) { - const std::string& body = params->post_body(); - scoped_ptr reader( - net::UploadOwnedBytesElementReader::CreateWithString(body)); - request->set_upload( - net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); - } - if (params->post_id() >= 0) { - // The POST in this case does not have an actual body, and only works - // when retrieving data from cache. This is done because we don't want - // to do a re-POST without user consent, and currently don't have a good - // plan on how to display the UI for that. - DCHECK(params->prefer_cache()); - DCHECK_EQ("POST", params->method()); - std::vector> element_readers; - request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream( - std::move(element_readers), params->post_id()))); - } - // If we're not at the beginning of the file, retrieve only the remaining - // portion. - bool has_last_modified = !params->last_modified().empty(); - bool has_etag = !params->etag().empty(); - - // If we've asked for a range, we want to make sure that we only - // get that range if our current copy of the information is good. - // We shouldn't be asked to continue if we don't have a verifier. - DCHECK(params->offset() == 0 || has_etag || has_last_modified); - - if (params->offset() > 0 && (has_etag || has_last_modified)) { - request->SetExtraRequestHeaderByName( - "Range", - base::StringPrintf("bytes=%" PRId64 "-", params->offset()), - true); - - // In accordance with RFC 2616 Section 14.27, use If-Range to specify that - // the server return the entire entity if the validator doesn't match. - // Last-Modified can be used in the absence of ETag as a validator if the - // response headers satisfied the HttpUtil::HasStrongValidators() predicate. - // - // This function assumes that HasStrongValidators() was true and that the - // ETag and Last-Modified header values supplied are valid. - request->SetExtraRequestHeaderByName( - "If-Range", has_etag ? params->etag() : params->last_modified(), true); + scoped_ptr url_request = + DownloadRequestCore::CreateRequestOnIOThread(download_id, params.get()); + scoped_ptr blob_data_handle = + params->GetBlobDataHandle(); + if (blob_data_handle) { + storage::BlobProtocolHandler::SetRequestedBlobDataHandle( + url_request.get(), std::move(blob_data_handle)); } - for (DownloadUrlParameters::RequestHeadersType::const_iterator iter - = params->request_headers_begin(); - iter != params->request_headers_end(); - ++iter) { - request->SetExtraRequestHeaderByName( - iter->first, iter->second, false /*overwrite*/); - } - - scoped_ptr save_info(new DownloadSaveInfo()); - save_info->file_path = params->file_path(); - save_info->suggested_name = params->suggested_name(); - save_info->offset = params->offset(); - save_info->hash_state = params->hash_state(); - save_info->prompt_for_save_location = params->prompt(); - save_info->file = params->GetFile(); - + // If there's a valid renderer process associated with the request, then the + // request should be driven by the ResourceLoader. Pass it over to the + // ResourceDispatcherHostImpl which will in turn pass it along to the + // ResourceLoader. if (params->render_process_host_id() != -1) { - ResourceDispatcherHost::Get()->BeginDownload( - std::move(request), params->referrer(), params->content_initiated(), - params->resource_context(), params->render_process_host_id(), - params->render_view_host_routing_id(), - params->render_frame_host_routing_id(), params->prefer_cache(), - params->do_not_prompt_for_login(), std::move(save_info), download_id, - params->callback()); + DownloadInterruptReason reason = + ResourceDispatcherHostImpl::Get()->BeginDownload( + std::move(url_request), params->referrer(), + params->content_initiated(), params->resource_context(), + params->render_process_host_id(), + params->render_view_host_routing_id(), + params->render_frame_host_routing_id(), + params->do_not_prompt_for_login()); + + // If the download was accepted, the DownloadResourceHandler is now + // responsible for driving the request to completion. + if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) + return nullptr; + + // Otherwise, create an interrupted download. + scoped_ptr failed_created_info( + new DownloadCreateInfo(base::Time::Now(), net::BoundNetLog(), + make_scoped_ptr(new DownloadSaveInfo))); + failed_created_info->url_chain.push_back(params->url()); + failed_created_info->result = reason; + scoped_ptr empty_byte_stream; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManager::StartDownload, download_manager, + base::Passed(&failed_created_info), + base::Passed(&empty_byte_stream), params->callback())); return nullptr; } + return scoped_ptr( - UrlDownloader::BeginDownload(download_manager, std::move(request), - params->referrer(), params->prefer_cache(), - std::move(save_info), download_id, - params->callback()) + UrlDownloader::BeginDownload(download_manager, std::move(url_request), + params->referrer()) .release()); } @@ -149,6 +113,7 @@ class DownloadItemFactoryImpl : public DownloadItemFactory { DownloadItemImpl* CreatePersistedItem( DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t download_id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -162,31 +127,33 @@ class DownloadItemFactoryImpl : public DownloadItemFactory { const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, bool opened, const net::BoundNetLog& bound_net_log) override { - return new DownloadItemImpl( - delegate, - download_id, - current_path, - target_path, - url_chain, - referrer_url, - mime_type, - original_mime_type, - start_time, - end_time, - etag, - last_modified, - received_bytes, - total_bytes, - state, - danger_type, - interrupt_reason, - opened, - bound_net_log); + return new DownloadItemImpl(delegate, + guid, + download_id, + current_path, + target_path, + url_chain, + referrer_url, + mime_type, + original_mime_type, + start_time, + end_time, + etag, + last_modified, + received_bytes, + total_bytes, + hash, + state, + danger_type, + interrupt_reason, + opened, + bound_net_log); } DownloadItemImpl* CreateActiveItem( @@ -240,6 +207,7 @@ DownloadItemImpl* DownloadManagerImpl::CreateActiveItem( DownloadItemImpl* download = item_factory_->CreateActiveItem(this, id, info, bound_net_log); downloads_[id] = download; + downloads_by_guid_[download->GetGuid()] = download; return download; } @@ -322,13 +290,13 @@ void DownloadManagerImpl::Shutdown() { // dangerous downloads which will remain in history if they aren't explicitly // accepted or discarded. Canceling will remove the intermediate download // file. - for (DownloadMap::iterator it = downloads_.begin(); it != downloads_.end(); - ++it) { - DownloadItemImpl* download = it->second; + for (const auto& it : downloads_) { + DownloadItemImpl* download = it.second; if (download->GetState() == DownloadItem::IN_PROGRESS) download->Cancel(false); } STLDeleteValues(&downloads_); + downloads_by_guid_.clear(); url_downloaders_.clear(); // We'll have nothing more to report to the observers after this point. @@ -345,6 +313,11 @@ void DownloadManagerImpl::StartDownload( const DownloadUrlParameters::OnStartedCallback& on_started) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(info); + // |stream| is only non-nil if the download request was successful. + DCHECK((info->result == DOWNLOAD_INTERRUPT_REASON_NONE && stream.get()) || + (info->result != DOWNLOAD_INTERRUPT_REASON_NONE && !stream.get())); + DVLOG(20) << __FUNCTION__ << "()" + << " result=" << DownloadInterruptReasonToString(info->result); uint32_t download_id = info->download_id; const bool new_download = (download_id == content::DownloadItem::kInvalidId); base::Callback got_id(base::Bind( @@ -380,13 +353,12 @@ void DownloadManagerImpl::StartDownloadWithId( if (!on_started.is_null()) on_started.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); // The ByteStreamReader lives and dies on the FILE thread. - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, - stream.release()); + if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, + stream.release()); return; } download = item_iterator->second; - DCHECK_EQ(download->GetState(), DownloadItem::IN_PROGRESS); - download->MergeOriginInfoOnResume(*info); } base::FilePath default_download_directory; @@ -397,20 +369,23 @@ void DownloadManagerImpl::StartDownloadWithId( &default_download_directory, &skip_dir_check); } - // Create the download file and start the download. - scoped_ptr download_file(file_factory_->CreateFile( - std::move(info->save_info), default_download_directory, info->url(), - info->referrer_url, delegate_ && delegate_->GenerateFileHash(), - std::move(stream), download->GetBoundNetLog(), - download->DestinationObserverAsWeakPtr())); - - // Attach the client ID identifying the app to the AV system. - if (download_file.get() && delegate_) { - download_file->SetClientGuid( - delegate_->ApplicationClientIdForFileScanning()); + scoped_ptr download_file; + + if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) { + DCHECK(stream.get()); + download_file.reset( + file_factory_->CreateFile(std::move(info->save_info), + default_download_directory, + std::move(stream), + download->GetBoundNetLog(), + download->DestinationObserverAsWeakPtr())); } + // It is important to leave info->save_info intact in the case of an interrupt + // so that the DownloadItem can salvage what it can out of a failed resumption + // attempt. - download->Start(std::move(download_file), std::move(info->request_handle)); + download->Start(std::move(download_file), std::move(info->request_handle), + *info); // For interrupted downloads, Start() will transition the state to // IN_PROGRESS and consumers will be notified via OnDownloadUpdated(). @@ -426,9 +401,8 @@ void DownloadManagerImpl::StartDownloadWithId( void DownloadManagerImpl::CheckForHistoryFilesRemoval() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { - DownloadItemImpl* item = it->second; + for (const auto& it : downloads_) { + DownloadItemImpl* item = it.second; CheckForFileRemoval(item); } } @@ -487,6 +461,8 @@ void DownloadManagerImpl::CreateSavePackageDownloadItemWithId( this, id, main_file_path, page_url, mime_type, std::move(request_handle), bound_net_log); downloads_[download_item->GetId()] = download_item; + DCHECK(!ContainsKey(downloads_by_guid_, download_item->GetGuid())); + downloads_by_guid_[download_item->GetGuid()] = download_item; FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated( this, download_item)); if (!item_created.is_null()) @@ -532,6 +508,8 @@ void DownloadManagerImpl::DownloadRemoved(DownloadItemImpl* download) { if (!download) return; + downloads_by_guid_.erase(download->GetGuid()); + uint32_t download_id = download->GetId(); if (downloads_.erase(download_id) == 0) return; @@ -556,19 +534,18 @@ void DownloadManagerImpl::RemoveUrlDownloader(UrlDownloader* downloader) { namespace { -bool RemoveDownloadBetween(base::Time remove_begin, - base::Time remove_end, - const DownloadItemImpl* download_item) { - return download_item->GetStartTime() >= remove_begin && - (remove_end.is_null() || download_item->GetStartTime() < remove_end); +bool EmptyFilter(const GURL& url) { + return true; } -bool RemoveDownloadByOriginAndTime(const url::Origin& origin, - base::Time remove_begin, - base::Time remove_end, - const DownloadItemImpl* download_item) { - return origin.IsSameOriginWith(url::Origin(download_item->GetURL())) && - RemoveDownloadBetween(remove_begin, remove_end, download_item); +bool RemoveDownloadByURLAndTime( + const base::Callback& url_filter, + base::Time remove_begin, + base::Time remove_end, + const DownloadItemImpl* download_item) { + return url_filter.Run(download_item->GetURL()) && + download_item->GetStartTime() >= remove_begin && + (remove_end.is_null() || download_item->GetStartTime() < remove_end); } } // namespace @@ -591,28 +568,21 @@ int DownloadManagerImpl::RemoveDownloads(const DownloadRemover& remover) { return count; } -int DownloadManagerImpl::RemoveDownloadsByOriginAndTime( - const url::Origin& origin, +int DownloadManagerImpl::RemoveDownloadsByURLAndTime( + const base::Callback& url_filter, base::Time remove_begin, base::Time remove_end) { - return RemoveDownloads(base::Bind(&RemoveDownloadByOriginAndTime, - base::ConstRef(origin), remove_begin, - remove_end)); -} - -int DownloadManagerImpl::RemoveDownloadsBetween(base::Time remove_begin, - base::Time remove_end) { - return RemoveDownloads( - base::Bind(&RemoveDownloadBetween, remove_begin, remove_end)); -} - -int DownloadManagerImpl::RemoveDownloads(base::Time remove_begin) { - return RemoveDownloadsBetween(remove_begin, base::Time()); + return RemoveDownloads(base::Bind(&RemoveDownloadByURLAndTime, + url_filter, + remove_begin, remove_end)); } int DownloadManagerImpl::RemoveAllDownloads() { + const base::Callback empty_filter = + base::Bind(&EmptyFilter); // The null times make the date range unbounded. - int num_deleted = RemoveDownloadsBetween(base::Time(), base::Time()); + int num_deleted = RemoveDownloadsByURLAndTime( + empty_filter, base::Time(), base::Time()); RecordClearAllSize(num_deleted); return num_deleted; } @@ -641,6 +611,7 @@ void DownloadManagerImpl::RemoveObserver(Observer* observer) { } DownloadItem* DownloadManagerImpl::CreateDownloadItem( + const std::string& guid, uint32_t id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -654,6 +625,7 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem( const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, @@ -662,8 +634,10 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem( NOTREACHED(); return NULL; } + DCHECK(!ContainsKey(downloads_by_guid_, guid)); DownloadItemImpl* item = item_factory_->CreatePersistedItem( this, + guid, id, current_path, target_path, @@ -677,12 +651,14 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem( last_modified, received_bytes, total_bytes, + hash, state, danger_type, interrupt_reason, opened, net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD)); downloads_[id] = item; + downloads_by_guid_[guid] = item; FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(this, item)); DVLOG(20) << __FUNCTION__ << "() download = " << item->DebugString(true); return item; @@ -690,9 +666,8 @@ DownloadItem* DownloadManagerImpl::CreateDownloadItem( int DownloadManagerImpl::InProgressCount() const { int count = 0; - for (DownloadMap::const_iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { - if (it->second->GetState() == DownloadItem::IN_PROGRESS) + for (const auto& it : downloads_) { + if (it.second->GetState() == DownloadItem::IN_PROGRESS) ++count; } return count; @@ -700,13 +675,12 @@ int DownloadManagerImpl::InProgressCount() const { int DownloadManagerImpl::NonMaliciousInProgressCount() const { int count = 0; - for (DownloadMap::const_iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { - if (it->second->GetState() == DownloadItem::IN_PROGRESS && - it->second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_URL && - it->second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT && - it->second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST && - it->second->GetDangerType() != + for (const auto& it : downloads_) { + if (it.second->GetState() == DownloadItem::IN_PROGRESS && + it.second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_URL && + it.second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT && + it.second->GetDangerType() != DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST && + it.second->GetDangerType() != DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED) { ++count; } @@ -715,21 +689,26 @@ int DownloadManagerImpl::NonMaliciousInProgressCount() const { } DownloadItem* DownloadManagerImpl::GetDownload(uint32_t download_id) { - return ContainsKey(downloads_, download_id) ? downloads_[download_id] : NULL; + return ContainsKey(downloads_, download_id) ? downloads_[download_id] + : nullptr; +} + +DownloadItem* DownloadManagerImpl::GetDownloadByGuid(const std::string& guid) { + DCHECK(guid == base::ToUpperASCII(guid)); + return ContainsKey(downloads_by_guid_, guid) ? downloads_by_guid_[guid] + : nullptr; } void DownloadManagerImpl::GetAllDownloads(DownloadVector* downloads) { - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { - downloads->push_back(it->second); + for (const auto& it : downloads_) { + downloads->push_back(it.second); } } void DownloadManagerImpl::OpenDownload(DownloadItemImpl* download) { int num_unopened = 0; - for (DownloadMap::iterator it = downloads_.begin(); - it != downloads_.end(); ++it) { - DownloadItemImpl* item = it->second; + for (const auto& it : downloads_) { + DownloadItemImpl* item = it.second; if ((item->GetState() == DownloadItem::COMPLETE) && !item->GetOpened()) ++num_unopened; diff --git a/chromium/content/browser/download/download_manager_impl.h b/chromium/content/browser/download/download_manager_impl.h index ebac07ab081..610b76408fc 100644 --- a/chromium/content/browser/download/download_manager_impl.h +++ b/chromium/content/browser/download/download_manager_impl.h @@ -7,13 +7,12 @@ #include -#include #include #include +#include #include #include "base/callback_forward.h" -#include "base/containers/hash_tables.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -42,7 +41,7 @@ class DownloadRequestHandleInterface; class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, private DownloadItemImplDelegate { public: - typedef base::Callback DownloadItemImplCreated; + using DownloadItemImplCreated = base::Callback; // Caller guarantees that |net_log| will remain valid // for the lifetime of DownloadManagerImpl (until Shutdown() is called). @@ -74,17 +73,16 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, scoped_ptr stream, const DownloadUrlParameters::OnStartedCallback& on_started) override; - int RemoveDownloadsByOriginAndTime(const url::Origin& origin, - base::Time remove_begin, - base::Time remove_end) override; - int RemoveDownloadsBetween(base::Time remove_begin, - base::Time remove_end) override; - int RemoveDownloads(base::Time remove_begin) override; + int RemoveDownloadsByURLAndTime( + const base::Callback& url_filter, + base::Time remove_begin, + base::Time remove_end) override; int RemoveAllDownloads() override; void DownloadUrl(scoped_ptr params) override; void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; content::DownloadItem* CreateDownloadItem( + const std::string& guid, uint32_t id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -98,6 +96,7 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, content::DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, @@ -107,6 +106,7 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, BrowserContext* GetBrowserContext() const override; void CheckForHistoryFilesRemoval() override; DownloadItem* GetDownload(uint32_t id) override; + DownloadItem* GetDownloadByGuid(const std::string& guid) override; // For testing; specifically, accessed from TestFileErrorInjector. void SetDownloadItemFactoryForTesting( @@ -118,10 +118,11 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, void RemoveUrlDownloader(UrlDownloader* downloader); private: - typedef std::set DownloadSet; - typedef base::hash_map DownloadMap; - typedef std::vector DownloadItemImplVector; - typedef base::Callback DownloadRemover; + using DownloadSet = std::set; + using DownloadMap = std::unordered_map; + using DownloadGuidMap = std::unordered_map; + using DownloadItemImplVector = std::vector; + using DownloadRemover = base::Callback; // For testing. friend class DownloadManagerTest; @@ -189,8 +190,16 @@ class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, // DownloadManager. This includes downloads started by the user in // this session, downloads initialized from the history system, and // "save page as" downloads. + // TODO(asanka): Remove this container in favor of downloads_by_guid_ as a + // part of http://crbug.com/593020. DownloadMap downloads_; + // Same as the above, but maps from GUID to download item. Note that the + // container is case sensitive. Hence the key needs to be normalized to + // upper-case when inserting new elements here. Fortunately for us, + // DownloadItemImpl already normalizes the string GUID. + DownloadGuidMap downloads_by_guid_; + int history_size_; // True if the download manager has been initialized and requires a shutdown. diff --git a/chromium/content/browser/download/download_manager_impl_unittest.cc b/chromium/content/browser/download/download_manager_impl_unittest.cc index 78d13863470..c72a1d07a82 100644 --- a/chromium/content/browser/download/download_manager_impl_unittest.cc +++ b/chromium/content/browser/download/download_manager_impl_unittest.cc @@ -6,12 +6,14 @@ #include #include + #include #include #include #include "base/bind.h" #include "base/files/scoped_temp_dir.h" +#include "base/guid.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -37,7 +39,6 @@ #include "content/public/test/mock_download_item.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread.h" -#include "net/base/net_util.h" #include "net/log/net_log.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock_mutant.h" @@ -50,6 +51,7 @@ using ::testing::Eq; using ::testing::Ref; using ::testing::Return; using ::testing::ReturnRef; +using ::testing::ReturnRefOfCopy; using ::testing::SetArgPointee; using ::testing::StrictMock; using ::testing::_; @@ -76,26 +78,27 @@ class MockDownloadItemImpl : public DownloadItemImpl { public: // Use history constructor for minimal base object. explicit MockDownloadItemImpl(DownloadItemImplDelegate* delegate) - : DownloadItemImpl( - delegate, - content::DownloadItem::kInvalidId, - base::FilePath(), - base::FilePath(), - std::vector(), - GURL(), - "application/octet-stream", - "application/octet-stream", - base::Time(), - base::Time(), - std::string(), - std::string(), - 0, - 0, - DownloadItem::COMPLETE, - DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - DOWNLOAD_INTERRUPT_REASON_NONE, - false, - net::BoundNetLog()) {} + : DownloadItemImpl(delegate, + std::string("7d122682-55b5-4a47-a253-36cadc3e5bee"), + content::DownloadItem::kInvalidId, + base::FilePath(), + base::FilePath(), + std::vector(), + GURL(), + "application/octet-stream", + "application/octet-stream", + base::Time(), + base::Time(), + std::string(), + std::string(), + 0, + 0, + std::string(), + DownloadItem::COMPLETE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_NONE, + false, + net::BoundNetLog()) {} virtual ~MockDownloadItemImpl() {} MOCK_METHOD4(OnDownloadTargetDetermined, @@ -114,10 +117,13 @@ class MockDownloadItemImpl : public DownloadItemImpl { MOCK_METHOD3(UpdateProgress, void(int64_t, int64_t, const std::string&)); MOCK_METHOD1(Cancel, void(bool)); MOCK_METHOD0(MarkAsComplete, void()); - MOCK_METHOD1(OnAllDataSaved, void(const std::string&)); + void OnAllDataSaved(int64_t, scoped_ptr) override { + NOTREACHED(); + } MOCK_METHOD0(OnDownloadedFileRemoved, void()); void Start(scoped_ptr download_file, - scoped_ptr req_handle) override { + scoped_ptr req_handle, + const DownloadCreateInfo& create_info) override { MockStart(download_file.get(), req_handle.get()); } @@ -153,6 +159,7 @@ class MockDownloadItemImpl : public DownloadItemImpl { MOCK_CONST_METHOD0(GetHashState, const std::string&()); MOCK_CONST_METHOD0(GetHash, const std::string&()); MOCK_CONST_METHOD0(GetId, uint32_t()); + MOCK_CONST_METHOD0(GetGuid, const std::string&()); MOCK_CONST_METHOD0(GetStartTime, base::Time()); MOCK_CONST_METHOD0(GetEndTime, base::Time()); MOCK_METHOD0(GetDownloadManager, DownloadManager*()); @@ -199,7 +206,6 @@ class MockDownloadManagerDelegate : public DownloadManagerDelegate { bool(DownloadItem*, const base::Closure&)); MOCK_METHOD2(ShouldOpenDownload, bool(DownloadItem*, const DownloadOpenDelayedCallback&)); - MOCK_METHOD0(GenerateFileHash, bool()); MOCK_METHOD4(GetSaveDir, void(BrowserContext*, base::FilePath*, base::FilePath*, bool*)); MOCK_METHOD5(ChooseSavePath, void( @@ -237,6 +243,7 @@ class MockDownloadItemFactory // Overridden methods from DownloadItemFactory. DownloadItemImpl* CreatePersistedItem( DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t download_id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -250,6 +257,7 @@ class MockDownloadItemFactory const std::string& last_modofied, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, @@ -304,6 +312,7 @@ void MockDownloadItemFactory::RemoveItem(int id) { DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem( DownloadItemImplDelegate* delegate, + const std::string& guid, uint32_t download_id, const base::FilePath& current_path, const base::FilePath& target_path, @@ -317,6 +326,7 @@ DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem( const std::string& last_modified, int64_t received_bytes, int64_t total_bytes, + const std::string& hash, DownloadItem::DownloadState state, DownloadDangerType danger_type, DownloadInterruptReason interrupt_reason, @@ -327,6 +337,7 @@ DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem( new StrictMock(&item_delegate_); EXPECT_CALL(*result, GetId()) .WillRepeatedly(Return(download_id)); + EXPECT_CALL(*result, GetGuid()).WillRepeatedly(ReturnRefOfCopy(guid)); items_[download_id] = result; return result; } @@ -342,6 +353,9 @@ DownloadItemImpl* MockDownloadItemFactory::CreateActiveItem( new StrictMock(&item_delegate_); EXPECT_CALL(*result, GetId()) .WillRepeatedly(Return(download_id)); + EXPECT_CALL(*result, GetGuid()) + .WillRepeatedly( + ReturnRefOfCopy(base::ToUpperASCII(base::GenerateGUID()))); items_[download_id] = result; // Active items are created and then immediately are called to start @@ -378,32 +392,24 @@ class MockDownloadFileFactory virtual ~MockDownloadFileFactory() {} // Overridden method from DownloadFileFactory - MOCK_METHOD8(MockCreateFile, MockDownloadFile*( - const DownloadSaveInfo&, - const base::FilePath&, - const GURL&, const GURL&, bool, - ByteStreamReader*, - const net::BoundNetLog&, - base::WeakPtr)); + MOCK_METHOD2(MockCreateFile, + MockDownloadFile*(const DownloadSaveInfo&, ByteStreamReader*)); virtual DownloadFile* CreateFile( scoped_ptr save_info, const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr stream, + scoped_ptr byte_stream, const net::BoundNetLog& bound_net_log, - base::WeakPtr observer) { - return MockCreateFile(*save_info.get(), default_download_directory, url, - referrer_url, calculate_hash, - stream.get(), bound_net_log, observer); + base::WeakPtr observer) override { + return MockCreateFile(*save_info, byte_stream.get()); } }; class MockBrowserContext : public BrowserContext { public: - MockBrowserContext() {} + MockBrowserContext() { + content::BrowserContext::Initialize(this, base::FilePath()); + } ~MockBrowserContext() {} MOCK_CONST_METHOD0(GetPath, base::FilePath()); @@ -411,8 +417,6 @@ class MockBrowserContext : public BrowserContext { ZoomLevelDelegate*(const base::FilePath&)); MOCK_CONST_METHOD0(IsOffTheRecord, bool()); MOCK_METHOD0(GetRequestContext, net::URLRequestContextGetter*()); - MOCK_METHOD1(GetRequestContextForRenderProcess, - net::URLRequestContextGetter*(int renderer_child_id)); MOCK_METHOD0(GetMediaRequestContext, net::URLRequestContextGetter*()); MOCK_METHOD1(GetMediaRequestContextForRenderProcess, @@ -429,6 +433,23 @@ class MockBrowserContext : public BrowserContext { MOCK_METHOD0(GetPermissionManager, PermissionManager*()); MOCK_METHOD0(GetBackgroundSyncController, BackgroundSyncController*()); + // Define these two methods to avoid a + // cannot access private member declared in class 'ScopedVector' + // build error if they're put in MOCK_METHOD. + net::URLRequestContextGetter* CreateRequestContext( + ProtocolHandlerMap* protocol_handlers, + URLRequestInterceptorScopedVector request_interceptors) override { + return nullptr; + } + + net::URLRequestContextGetter* CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + ProtocolHandlerMap* protocol_handlers, + URLRequestInterceptorScopedVector request_interceptors) override { + return nullptr; + } + scoped_ptr CreateZoomLevelDelegate( const base::FilePath& path) override { return scoped_ptr(CreateZoomLevelDelegateMock(path)); @@ -445,6 +466,14 @@ class MockDownloadManagerObserver : public DownloadManager::Observer { MOCK_METHOD2(SelectFileDialogDisplayed, void(DownloadManager*, int32_t)); }; +class MockByteStreamReader : public ByteStreamReader { + public: + virtual ~MockByteStreamReader() {} + MOCK_METHOD2(Read, StreamState(scoped_refptr*, size_t*)); + MOCK_CONST_METHOD0(GetStatus, int()); + MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); +}; + } // namespace class DownloadManagerTest : public testing::Test { @@ -529,7 +558,7 @@ class DownloadManagerTest : public testing::Test { // we call Start on it immediately, so we need to set that expectation // in the factory. scoped_ptr req_handle; - item.Start(scoped_ptr(), std::move(req_handle)); + item.Start(scoped_ptr(), std::move(req_handle), info); DCHECK(id < download_urls_.size()); EXPECT_CALL(item, GetURL()).WillRepeatedly(ReturnRef(download_urls_[id])); @@ -605,7 +634,7 @@ class DownloadManagerTest : public testing::Test { // Confirm the appropriate invocations occur when you start a download. TEST_F(DownloadManagerTest, StartDownload) { scoped_ptr info(new DownloadCreateInfo); - scoped_ptr stream; + scoped_ptr stream(new MockByteStreamReader); uint32_t local_id(5); // Random value base::FilePath download_path(FILE_PATH_LITERAL("download/path")); @@ -618,16 +647,12 @@ TEST_F(DownloadManagerTest, StartDownload) { // Doing nothing will set the default download directory to null. EXPECT_CALL(GetMockDownloadManagerDelegate(), GetSaveDir(_, _, _, _)); - EXPECT_CALL(GetMockDownloadManagerDelegate(), GenerateFileHash()) - .WillOnce(Return(true)); EXPECT_CALL(GetMockDownloadManagerDelegate(), ApplicationClientIdForFileScanning()) .WillRepeatedly(Return("client-id")); MockDownloadFile* mock_file = new MockDownloadFile; - EXPECT_CALL(*mock_file, SetClientGuid("client-id")); EXPECT_CALL(*mock_download_file_factory_.get(), - MockCreateFile(Ref(*info->save_info.get()), _, _, _, true, - stream.get(), _, _)) + MockCreateFile(Ref(*info->save_info.get()), stream.get())) .WillOnce(Return(mock_file)); download_manager_->StartDownload(std::move(info), std::move(stream), @@ -707,8 +732,53 @@ TEST_F(DownloadManagerTest, RemoveAllDownloads) { // result in them being removed from the DownloadManager list. } -// Confirm that only downloads with same origin are removed. -TEST_F(DownloadManagerTest, RemoveSameOriginDownloads) { +TEST_F(DownloadManagerTest, GetDownloadByGuid) { + for (uint32_t i = 0; i < 4; ++i) + AddItemToManager(); + + MockDownloadItemImpl& item = GetMockDownloadItem(0); + DownloadItem* result = download_manager_->GetDownloadByGuid(item.GetGuid()); + ASSERT_TRUE(result); + ASSERT_EQ(static_cast(&item), result); + + ASSERT_FALSE(download_manager_->GetDownloadByGuid("")); + + const char kGuid[] = "8DF158E8-C980-4618-BB03-EBA3242EB48B"; + DownloadItem* persisted_item = download_manager_->CreateDownloadItem( + kGuid, + 10, + base::FilePath(), + base::FilePath(), + std::vector(), + GURL("http://example.com/a"), + "application/octet-stream", + "application/octet-stream", + base::Time::Now(), + base::Time::Now(), + std::string(), + std::string(), + 10, + 10, + std::string(), + DownloadItem::INTERRUPTED, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, + false); + ASSERT_TRUE(persisted_item); + + ASSERT_EQ(persisted_item, download_manager_->GetDownloadByGuid(kGuid)); +} + +namespace { + +base::Callback GetSingleURLFilter(const GURL& url) { + return base::Bind(&GURL::operator==, base::Owned(new GURL(url))); +} + +} // namespace + +// Confirm that only downloads with the specified URL are removed. +TEST_F(DownloadManagerTest, RemoveDownloadsByURL) { base::Time now(base::Time::Now()); for (uint32_t i = 0; i < 2; ++i) { MockDownloadItemImpl& item(AddItemToManager()); @@ -720,9 +790,10 @@ TEST_F(DownloadManagerTest, RemoveSameOriginDownloads) { EXPECT_CALL(GetMockDownloadItem(0), Remove()); EXPECT_CALL(GetMockDownloadItem(1), Remove()).Times(0); - url::Origin origin_to_clear(download_urls_[0]); - int remove_count = download_manager_->RemoveDownloadsByOriginAndTime( - origin_to_clear, base::Time(), base::Time::Max()); + base::Callback url_filter = + GetSingleURLFilter(download_urls_[0]); + int remove_count = download_manager_->RemoveDownloadsByURLAndTime( + url_filter, base::Time(), base::Time::Max()); EXPECT_EQ(remove_count, 1); } diff --git a/chromium/content/browser/download/download_net_log_parameters.cc b/chromium/content/browser/download/download_net_log_parameters.cc index e81c813af2f..110bc98c3b0 100644 --- a/chromium/content/browser/download/download_net_log_parameters.cc +++ b/chromium/content/browser/download/download_net_log_parameters.cc @@ -89,14 +89,11 @@ scoped_ptr ItemRenamedNetLogCallback( scoped_ptr ItemInterruptedNetLogCallback( DownloadInterruptReason reason, int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode) { scoped_ptr dict(new base::DictionaryValue()); dict->SetString("interrupt_reason", DownloadInterruptReasonToString(reason)); dict->SetString("bytes_so_far", base::Int64ToString(bytes_so_far)); - dict->SetString("hash_state", - base::HexEncode(hash_state->data(), hash_state->size())); return std::move(dict); } @@ -105,15 +102,12 @@ scoped_ptr ItemResumingNetLogCallback( bool user_initiated, DownloadInterruptReason reason, int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode) { scoped_ptr dict(new base::DictionaryValue()); dict->SetString("user_initiated", user_initiated ? "true" : "false"); dict->SetString("interrupt_reason", DownloadInterruptReasonToString(reason)); dict->SetString("bytes_so_far", base::Int64ToString(bytes_so_far)); - dict->SetString("hash_state", - base::HexEncode(hash_state->data(), hash_state->size())); return std::move(dict); } @@ -143,13 +137,10 @@ scoped_ptr ItemFinishedNetLogCallback( scoped_ptr ItemCanceledNetLogCallback( int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode) { scoped_ptr dict(new base::DictionaryValue()); dict->SetString("bytes_so_far", base::Int64ToString(bytes_so_far)); - dict->SetString("hash_state", - base::HexEncode(hash_state->data(), hash_state->size())); return std::move(dict); } diff --git a/chromium/content/browser/download/download_net_log_parameters.h b/chromium/content/browser/download/download_net_log_parameters.h index 2f4b6a006e5..b10a345ed33 100644 --- a/chromium/content/browser/download/download_net_log_parameters.h +++ b/chromium/content/browser/download/download_net_log_parameters.h @@ -50,7 +50,6 @@ scoped_ptr ItemRenamedNetLogCallback( scoped_ptr ItemInterruptedNetLogCallback( DownloadInterruptReason reason, int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is resumed. @@ -58,7 +57,6 @@ scoped_ptr ItemResumingNetLogCallback( bool user_initiated, DownloadInterruptReason reason, int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadItem is completing. @@ -75,7 +73,6 @@ scoped_ptr ItemFinishedNetLogCallback( // Returns NetLog parameters when a DownloadItem is canceled. scoped_ptr ItemCanceledNetLogCallback( int64_t bytes_so_far, - const std::string* hash_state, net::NetLogCaptureMode capture_mode); // Returns NetLog parameters when a DownloadFile is opened. diff --git a/chromium/content/browser/download/download_request_core.cc b/chromium/content/browser/download/download_request_core.cc index 072ba7b0b55..3f70e956baa 100644 --- a/chromium/content/browser/download/download_request_core.cc +++ b/chromium/content/browser/download/download_request_core.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/format_macros.h" #include "base/location.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -21,41 +22,197 @@ #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_request_handle.h" #include "content/browser/download/download_stats.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager_delegate.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/power_save_blocker.h" +#include "content/public/browser/resource_context.h" #include "content/public/browser/web_contents.h" +#include "net/base/elements_upload_data_stream.h" #include "net/base/io_buffer.h" +#include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/upload_bytes_element_reader.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "net/url_request/url_request_context.h" namespace content { +namespace { + +// This is a UserData::Data that will be attached to a URLRequest as a +// side-channel for passing download parameters. +class DownloadRequestData : public base::SupportsUserData::Data { + public: + ~DownloadRequestData() override {} + + static void Attach(net::URLRequest* request, + DownloadUrlParameters* download_parameters, + uint32_t download_id); + static DownloadRequestData* Get(net::URLRequest* request); + static void Detach(net::URLRequest* request); + + scoped_ptr TakeSaveInfo() { return std::move(save_info_); } + uint32_t download_id() const { return download_id_; } + const DownloadUrlParameters::OnStartedCallback& callback() const { + return on_started_callback_; + } + + private: + static const int kKey; + + scoped_ptr save_info_; + uint32_t download_id_ = DownloadItem::kInvalidId; + DownloadUrlParameters::OnStartedCallback on_started_callback_; +}; + +// static +const int DownloadRequestData::kKey = 0; + +// static +void DownloadRequestData::Attach(net::URLRequest* request, + DownloadUrlParameters* parameters, + uint32_t download_id) { + DownloadRequestData* request_data = new DownloadRequestData; + request_data->save_info_.reset( + new DownloadSaveInfo(parameters->GetSaveInfo())); + request_data->download_id_ = download_id; + request_data->on_started_callback_ = parameters->callback(); + request->SetUserData(&kKey, request_data); +} + +// static +DownloadRequestData* DownloadRequestData::Get(net::URLRequest* request) { + return static_cast(request->GetUserData(&kKey)); +} + +// static +void DownloadRequestData::Detach(net::URLRequest* request) { + request->RemoveUserData(&kKey); +} + +} // namespace + const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; -DownloadRequestCore::DownloadRequestCore( - net::URLRequest* request, - scoped_ptr save_info, - const base::Closure& on_ready_to_read_callback) - : on_ready_to_read_callback_(on_ready_to_read_callback), +// static +scoped_ptr DownloadRequestCore::CreateRequestOnIOThread( + uint32_t download_id, + DownloadUrlParameters* params) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(download_id == DownloadItem::kInvalidId || + !params->content_initiated()) + << "Content initiated downloads shouldn't specify a download ID"; + + // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and + // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so + // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4. + scoped_ptr request( + params->resource_context()->GetRequestContext()->CreateRequest( + params->url(), net::DEFAULT_PRIORITY, nullptr)); + request->set_method(params->method()); + + if (!params->post_body().empty()) { + const std::string& body = params->post_body(); + scoped_ptr reader( + net::UploadOwnedBytesElementReader::CreateWithString(body)); + request->set_upload( + net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); + } + + if (params->post_id() >= 0) { + // The POST in this case does not have an actual body, and only works + // when retrieving data from cache. This is done because we don't want + // to do a re-POST without user consent, and currently don't have a good + // plan on how to display the UI for that. + DCHECK(params->prefer_cache()); + DCHECK_EQ("POST", params->method()); + std::vector> element_readers; + request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream( + std::move(element_readers), params->post_id()))); + } + + int load_flags = request->load_flags(); + if (params->prefer_cache()) { + // If there is upload data attached, only retrieve from cache because there + // is no current mechanism to prompt the user for their consent for a + // re-post. For GETs, try to retrieve data from the cache and skip + // validating the entry if present. + if (request->get_upload()) + load_flags |= net::LOAD_ONLY_FROM_CACHE; + else + load_flags |= net::LOAD_PREFERRING_CACHE; + } else { + load_flags |= net::LOAD_DISABLE_CACHE; + } + request->SetLoadFlags(load_flags); + + bool has_last_modified = !params->last_modified().empty(); + bool has_etag = !params->etag().empty(); + + // If we've asked for a range, we want to make sure that we only get that + // range if our current copy of the information is good. We shouldn't be + // asked to continue if we don't have a verifier. + DCHECK(params->offset() == 0 || has_etag || has_last_modified); + + // If we're not at the beginning of the file, retrieve only the remaining + // portion. + if (params->offset() > 0 && (has_etag || has_last_modified)) { + request->SetExtraRequestHeaderByName( + "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()), + true); + + // In accordance with RFC 2616 Section 14.27, use If-Range to specify that + // the server return the entire entity if the validator doesn't match. + // Last-Modified can be used in the absence of ETag as a validator if the + // response headers satisfied the HttpUtil::HasStrongValidators() predicate. + // + // This function assumes that HasStrongValidators() was true and that the + // ETag and Last-Modified header values supplied are valid. + request->SetExtraRequestHeaderByName( + "If-Range", has_etag ? params->etag() : params->last_modified(), true); + } + + for (const auto& header : params->request_headers()) + request->SetExtraRequestHeaderByName(header.first, header.second, + false /*overwrite*/); + + DownloadRequestData::Attach(request.get(), std::move(params), download_id); + return request; +} + +DownloadRequestCore::DownloadRequestCore(net::URLRequest* request, + Delegate* delegate) + : delegate_(delegate), request_(request), - save_info_(std::move(save_info)), + download_id_(DownloadItem::kInvalidId), last_buffer_size_(0), bytes_read_(0), pause_count_(0), - was_deferred_(false) { + was_deferred_(false), + is_partial_request_(false), + started_(false), + abort_reason_(DOWNLOAD_INTERRUPT_REASON_NONE) { DCHECK(request_); - DCHECK(save_info_); - DCHECK(!on_ready_to_read_callback_.is_null()); + DCHECK(delegate_); RecordDownloadCount(UNTHROTTLED_COUNT); power_save_blocker_ = PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress"); + DownloadRequestData* request_data = DownloadRequestData::Get(request_); + if (request_data) { + save_info_ = request_data->TakeSaveInfo(); + download_id_ = request_data->download_id(); + on_started_callback_ = request_data->callback(); + DownloadRequestData::Detach(request_); + is_partial_request_ = save_info_->offset > 0; + } else { + save_info_.reset(new DownloadSaveInfo); + } } DownloadRequestCore::~DownloadRequestCore() { @@ -68,15 +225,41 @@ DownloadRequestCore::~DownloadRequestCore() { base::TimeTicks::Now() - download_start_time_); } -// Send the download creation information to the download thread. -void DownloadRequestCore::OnResponseStarted( - scoped_ptr* create_info, - scoped_ptr* stream_reader) { +scoped_ptr DownloadRequestCore::CreateDownloadCreateInfo( + DownloadInterruptReason result) { + DCHECK(!started_); + started_ = true; + scoped_ptr create_info(new DownloadCreateInfo( + base::Time::Now(), request()->net_log(), std::move(save_info_))); + + if (result == DOWNLOAD_INTERRUPT_REASON_NONE) + create_info->remote_address = request()->GetSocketAddress().host(); + create_info->url_chain = request()->url_chain(); + create_info->referrer_url = GURL(request()->referrer()); + create_info->result = result; + create_info->download_id = download_id_; + return create_info; +} + +bool DownloadRequestCore::OnResponseStarted( + const std::string& override_mime_type) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(save_info_); DVLOG(20) << __FUNCTION__ << "()" << DebugString(); download_start_time_ = base::TimeTicks::Now(); + DownloadInterruptReason result = + request()->response_headers() + ? HandleSuccessfulServerResponse(*request()->response_headers(), + save_info_.get()) + : DOWNLOAD_INTERRUPT_REASON_NONE; + + scoped_ptr create_info = CreateDownloadCreateInfo(result); + if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { + delegate_->OnStart(std::move(create_info), scoped_ptr(), + base::ResetAndReturn(&on_started_callback_)); + return false; + } + // If it's a download, we don't want to poison the cache with it. request()->StopCaching(); @@ -90,35 +273,21 @@ void DownloadRequestCore::OnResponseStarted( int64_t content_length = request()->GetExpectedContentSize() > 0 ? request()->GetExpectedContentSize() : 0; - - // Deleted in DownloadManager. - scoped_ptr info( - new DownloadCreateInfo(base::Time::Now(), content_length, - request()->net_log(), std::move(save_info_))); + create_info->total_bytes = content_length; // Create the ByteStream for sending data to the download sink. + scoped_ptr stream_reader; CreateByteStream( base::ThreadTaskRunnerHandle::Get(), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), - kDownloadByteStreamSize, &stream_writer_, stream_reader); + kDownloadByteStreamSize, &stream_writer_, &stream_reader); stream_writer_->RegisterCallback( base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); - info->url_chain = request()->url_chain(); - info->referrer_url = GURL(request()->referrer()); - string mime_type; - request()->GetMimeType(&mime_type); - info->mime_type = mime_type; - info->remote_address = request()->GetSocketAddress().host(); - if (request()->response_headers()) { - // Grab the first content-disposition header. There may be more than one, - // though as of this writing, the network stack ensures if there are, they - // are all duplicates. - request()->response_headers()->EnumerateHeader( - nullptr, "Content-Disposition", &info->content_disposition); - } - RecordDownloadMimeType(info->mime_type); - RecordDownloadContentDisposition(info->content_disposition); + if (!override_mime_type.empty()) + create_info->mime_type = override_mime_type; + else + request()->GetMimeType(&create_info->mime_type); // Get the last modified time and etag. const net::HttpResponseHeaders* headers = request()->response_headers(); @@ -127,33 +296,49 @@ void DownloadRequestCore::OnResponseStarted( // If we don't have strong validators as per RFC 2616 section 13.3.3, then // we neither store nor use them for range requests. if (!headers->EnumerateHeader(nullptr, "Last-Modified", - &info->last_modified)) - info->last_modified.clear(); - if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag)) - info->etag.clear(); + &create_info->last_modified)) + create_info->last_modified.clear(); + if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag)) + create_info->etag.clear(); } - int status = headers->response_code(); - if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { - // Success & not range response; if we asked for a range, we didn't - // get it--reset the file pointers to reflect that. - info->save_info->offset = 0; - info->save_info->hash_state = ""; - } + // Grab the first content-disposition header. There may be more than one, + // though as of this writing, the network stack ensures if there are, they + // are all duplicates. + headers->EnumerateHeader(nullptr, "Content-Disposition", + &create_info->content_disposition); - if (!headers->GetMimeType(&info->original_mime_type)) - info->original_mime_type.clear(); + if (!headers->GetMimeType(&create_info->original_mime_type)) + create_info->original_mime_type.clear(); } // Blink verifies that the requester of this download is allowed to set a - // suggested name for the security origin of the downlaod URL. However, this + // suggested name for the security origin of the download URL. However, this // assumption doesn't hold if there were cross origin redirects. Therefore, // clear the suggested_name for such requests. - if (info->url_chain.size() > 1 && - info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) - info->save_info->suggested_name.clear(); + if (create_info->url_chain.size() > 1 && + create_info->url_chain.front().GetOrigin() != + create_info->url_chain.back().GetOrigin()) + create_info->save_info->suggested_name.clear(); + + RecordDownloadMimeType(create_info->mime_type); + RecordDownloadContentDisposition(create_info->content_disposition); + + delegate_->OnStart(std::move(create_info), std::move(stream_reader), + base::ResetAndReturn(&on_started_callback_)); + return true; +} - info.swap(*create_info); +bool DownloadRequestCore::OnRequestRedirected() { + DVLOG(20) << __FUNCTION__ << "() " << DebugString(); + if (is_partial_request_) { + // A redirect while attempting a partial resumption indicates a potential + // middle box. Trigger another interruption so that the DownloadItem can + // retry. + abort_reason_ = DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE; + return false; + } + return true; } // Create a new buffer, which will be handed to the download thread for file @@ -212,7 +397,13 @@ bool DownloadRequestCore::OnReadCompleted(int bytes_read, bool* defer) { return true; } -DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( +void DownloadRequestCore::OnWillAbort(DownloadInterruptReason reason) { + DVLOG(20) << __FUNCTION__ << "() reason=" << reason << " " << DebugString(); + DCHECK(!started_); + abort_reason_ = reason; +} + +void DownloadRequestCore::OnResponseCompleted( const net::URLRequestStatus& status) { DCHECK_CURRENTLY_ON(BrowserThread::IO); int response_code = status.is_success() ? request()->GetResponseCode() : 0; @@ -221,80 +412,27 @@ DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( << " status.error() = " << status.error() << " response_code = " << response_code; - net::Error error_code = net::OK; - if (status.status() == net::URLRequestStatus::FAILED || - // Note cancels as failures too. - status.status() == net::URLRequestStatus::CANCELED) { - error_code = static_cast(status.error()); // Normal case. - // Make sure that at least the fact of failure comes through. - if (error_code == net::OK) - error_code = net::ERR_FAILED; - } - - // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are - // allowed since a number of servers in the wild close the connection too - // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - - // treat downloads as complete in both cases, so we follow their lead. - if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || - error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { - error_code = net::OK; - } - DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( - error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); - - if (status.status() == net::URLRequestStatus::CANCELED && - status.error() == net::ERR_ABORTED) { - // CANCELED + ERR_ABORTED == something outside of the network - // stack cancelled the request. There aren't that many things that - // could do this to a download request (whose lifetime is separated from - // the tab from which it came). We map this to USER_CANCELLED as the - // case we know about (system suspend because of laptop close) corresponds - // to a user action. - // TODO(ahendrickson) -- Find a better set of codes to use here, as - // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. - if (net::IsCertStatusError(request()->ssl_info().cert_status)) - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; - else - reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; - } - - if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE && - request()->response_headers()) { - // Handle server's response codes. - switch (response_code) { - case -1: // Non-HTTP request. - case net::HTTP_OK: - case net::HTTP_CREATED: - case net::HTTP_ACCEPTED: - case net::HTTP_NON_AUTHORITATIVE_INFORMATION: - case net::HTTP_RESET_CONTENT: - case net::HTTP_PARTIAL_CONTENT: - // Expected successful codes. - break; - case net::HTTP_NO_CONTENT: - case net::HTTP_NOT_FOUND: - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; - break; - case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: - // Retry by downloading from the start automatically: - // If we haven't received data when we get this error, we won't. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; - break; - case net::HTTP_UNAUTHORIZED: - // Server didn't authorize this request. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; - break; - case net::HTTP_FORBIDDEN: - // Server forbids access to this resource. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; - break; - default: // All other errors. - // Redirection and informational codes should have been handled earlier - // in the stack. - DCHECK_NE(3, response_code / 100); - DCHECK_NE(1, response_code / 100); - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; - break; + DownloadInterruptReason reason = HandleRequestStatus(status); + + if (status.status() == net::URLRequestStatus::CANCELED) { + if (abort_reason_ != DOWNLOAD_INTERRUPT_REASON_NONE) { + // If a more specific interrupt reason was specified before the request + // was explicitly cancelled, then use it. + reason = abort_reason_; + } else if (status.error() == net::ERR_ABORTED) { + // CANCELED + ERR_ABORTED == something outside of the network + // stack cancelled the request. There aren't that many things that + // could do this to a download request (whose lifetime is separated from + // the tab from which it came). We map this to USER_CANCELLED as the + // case we know about (system suspend because of laptop close) corresponds + // to a user action. + // TODO(asanka): A lid close or other power event should result in an + // interruption that doesn't discard the partial state, unlike + // USER_CANCELLED. (https://crbug.com/166179) + if (net::IsCertStatusError(request()->ssl_info().cert_status)) + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; + else + reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; } } @@ -325,7 +463,16 @@ DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( stream_writer_.reset(); // We no longer need the stream. read_buffer_ = nullptr; - return reason; + if (started_) + return; + + // OnResponseCompleted() called without OnResponseStarted(). This should only + // happen when the request was aborted. + DCHECK_NE(reason, DOWNLOAD_INTERRUPT_REASON_NONE); + scoped_ptr create_info = CreateDownloadCreateInfo(reason); + scoped_ptr empty_byte_stream; + delegate_->OnStart(std::move(create_info), std::move(empty_byte_stream), + base::ResetAndReturn(&on_started_callback_)); } void DownloadRequestCore::PauseRequest() { @@ -351,16 +498,136 @@ void DownloadRequestCore::ResumeRequest() { last_stream_pause_time_ = base::TimeTicks(); } - on_ready_to_read_callback_.Run(); + delegate_->OnReadyToRead(); } std::string DownloadRequestCore::DebugString() const { return base::StringPrintf( "{" + " this=%p " " url_ = " "\"%s\"" " }", + reinterpret_cast(this), request() ? request()->url().spec().c_str() : ""); } +// static +DownloadInterruptReason DownloadRequestCore::HandleRequestStatus( + const net::URLRequestStatus& status) { + net::Error error_code = net::OK; + if (status.status() == net::URLRequestStatus::FAILED || + // Note cancels as failures too. + status.status() == net::URLRequestStatus::CANCELED) { + error_code = static_cast(status.error()); // Normal case. + // Make sure that at least the fact of failure comes through. + if (error_code == net::OK) + error_code = net::ERR_FAILED; + } + + // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are + // allowed since a number of servers in the wild close the connection too + // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - + // treat downloads as complete in both cases, so we follow their lead. + if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || + error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { + error_code = net::OK; + } + DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( + error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); + + return reason; +} + +// static +DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse( + const net::HttpResponseHeaders& http_headers, + DownloadSaveInfo* save_info) { + switch (http_headers.response_code()) { + case -1: // Non-HTTP request. + case net::HTTP_OK: + case net::HTTP_NON_AUTHORITATIVE_INFORMATION: + case net::HTTP_PARTIAL_CONTENT: + // Expected successful codes. + break; + + case net::HTTP_CREATED: + case net::HTTP_ACCEPTED: + // Per RFC 2616 the entity being transferred is metadata about the + // resource at the target URL and not the resource at that URL (or the + // resource that would be at the URL once processing is completed in the + // case of HTTP_ACCEPTED). However, we currently don't have special + // handling for these response and they are downloaded the same as a + // regular response. + break; + + case net::HTTP_NO_CONTENT: + case net::HTTP_RESET_CONTENT: + // These two status codes don't have an entity (or rather RFC 2616 + // requires that there be no entity). They are treated the same as the + // resource not being found since there is no entity to download. + + case net::HTTP_NOT_FOUND: + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + break; + + case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: + // Retry by downloading from the start automatically: + // If we haven't received data when we get this error, we won't. + return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; + break; + case net::HTTP_UNAUTHORIZED: + case net::HTTP_PROXY_AUTHENTICATION_REQUIRED: + // Server didn't authorize this request. + return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; + break; + case net::HTTP_FORBIDDEN: + // Server forbids access to this resource. + return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; + break; + default: // All other errors. + // Redirection and informational codes should have been handled earlier + // in the stack. + DCHECK_NE(3, http_headers.response_code() / 100); + DCHECK_NE(1, http_headers.response_code() / 100); + return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; + } + + if (save_info && save_info->offset > 0) { + // The caller is expecting a partial response. + + if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) { + // Requested a partial range, but received the entire response. + save_info->offset = 0; + save_info->hash_of_partial_file.clear(); + save_info->hash_state.reset(); + return DOWNLOAD_INTERRUPT_REASON_NONE; + } + + int64_t first_byte = -1; + int64_t last_byte = -1; + int64_t length = -1; + if (!http_headers.GetContentRange(&first_byte, &last_byte, &length)) + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + DCHECK_GE(first_byte, 0); + + if (first_byte != save_info->offset) { + // The server returned a different range than the one we requested. Assume + // the response is bad. + // + // In the future we should consider allowing offsets that are less than + // the offset we've requested, since in theory we can truncate the partial + // file at the offset and continue. + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + } + + return DOWNLOAD_INTERRUPT_REASON_NONE; + } + + if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT) + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + + return DOWNLOAD_INTERRUPT_REASON_NONE; +} + } // namespace content diff --git a/chromium/content/browser/download/download_request_core.h b/chromium/content/browser/download/download_request_core.h index 22ca862ef85..ac9e9b6cd86 100644 --- a/chromium/content/browser/download/download_request_core.h +++ b/chromium/content/browser/download/download_request_core.h @@ -20,7 +20,9 @@ #include "content/public/browser/download_url_parameters.h" namespace net { +class HttpResponseHeaders; class URLRequest; +class URLRequestStatus; } // namespace net namespace content { @@ -38,29 +40,29 @@ struct DownloadCreateInfo; class CONTENT_EXPORT DownloadRequestCore : public base::SupportsWeakPtr { public: - // Size of the buffer used between the DownloadRequestCore and the - // downstream receiver of its output. - static const int kDownloadByteStreamSize; - - // |request| *must* outlive the DownloadRequestCore. |save_info| must be - // valid. - // - // Invokes |on_ready_to_read_callback| if a previous call to OnReadCompleted() - // resulted in |defer| being set to true, and DownloadRequestCore is now ready - // to commence reading. - DownloadRequestCore(net::URLRequest* request, - scoped_ptr save_info, - const base::Closure& on_ready_to_read_callback); + class Delegate { + public: + virtual void OnReadyToRead() = 0; + virtual void OnStart( + scoped_ptr download_create_info, + scoped_ptr stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) = 0; + }; + + // All parameters are required. |request| and |delegate| must outlive + // DownloadRequestCore. + DownloadRequestCore(net::URLRequest* request, Delegate* delegate); ~DownloadRequestCore(); // Should be called when the URLRequest::Delegate receives OnResponseStarted. - // Constructs a DownloadCreateInfo and a ByteStreamReader that should be - // passed into DownloadManagerImpl::StartDownload(). - // - // Only populates the response derived fields of DownloadCreateInfo, with the - // exception of |save_info|. - void OnResponseStarted(scoped_ptr* info, - scoped_ptr* stream_reader); + // Invokes Delegate::OnStart() with download start parameters. The + // |override_mime_type| is used as the MIME type for the download when + // constructing a DownloadCreateInfo object. + bool OnResponseStarted(const std::string& override_mime_type); + + // Should be called to handle a redirect. The caller should only allow the + // redirect to be followed if the return value is true. + bool OnRequestRedirected(); // Starts a read cycle. Creates a new IOBuffer which can be passed into // URLRequest::Read(). Call OnReadCompleted() when the Read operation @@ -69,20 +71,22 @@ class CONTENT_EXPORT DownloadRequestCore int* buf_size, int min_size); + // Used to notify DownloadRequestCore that the caller is about to abort the + // outer request. |reason| will be used as the final interrupt reason when + // OnResponseCompleted() is called. + void OnWillAbort(DownloadInterruptReason reason); + // Should be called when the Read() operation completes. |defer| will be set // to true if reading is to be suspended. In the latter case, once more data // can be read, invokes the |on_ready_to_read_callback|. bool OnReadCompleted(int bytes_read, bool* defer); - // Called to signal that the response is complete. If the return value is - // something other than DOWNLOAD_INTERRUPT_REASON_NONE, then the download - // should be considered interrupted. + // Called to signal that the response is complete. // // It is expected that once this method is invoked, the DownloadRequestCore // object will be destroyed in short order without invoking any other methods // other than the destructor. - DownloadInterruptReason OnResponseCompleted( - const net::URLRequestStatus& status); + void OnResponseCompleted(const net::URLRequestStatus& status); // Called if the request should suspend reading. A subsequent // OnReadCompleted() will result in |defer| being set to true. @@ -98,13 +102,36 @@ class CONTENT_EXPORT DownloadRequestCore std::string DebugString() const; + static scoped_ptr CreateRequestOnIOThread( + uint32_t download_id, + DownloadUrlParameters* params); + + // Size of the buffer used between the DownloadRequestCore and the + // downstream receiver of its output. + static const int kDownloadByteStreamSize; + protected: net::URLRequest* request() const { return request_; } private: - base::Closure on_ready_to_read_callback_; + static DownloadInterruptReason HandleRequestStatus( + const net::URLRequestStatus& status); + + static DownloadInterruptReason HandleSuccessfulServerResponse( + const net::HttpResponseHeaders& http_headers, + DownloadSaveInfo* save_info); + + scoped_ptr CreateDownloadCreateInfo( + DownloadInterruptReason result); + + Delegate* delegate_; net::URLRequest* request_; + + // "Passthrough" fields. These are only kept here so that they can be used to + // populate the DownloadCreateInfo when the time comes. scoped_ptr save_info_; + uint32_t download_id_; + DownloadUrlParameters::OnStartedCallback on_started_callback_; // Data flow scoped_refptr read_buffer_; // From URLRequest. @@ -125,6 +152,15 @@ class CONTENT_EXPORT DownloadRequestCore int pause_count_; bool was_deferred_; + bool is_partial_request_; + bool started_; + + // When DownloadRequestCore initiates an abort (by blocking a redirect, for + // example) it expects to eventually receive a OnResponseCompleted() with a + // status indicating that the request was aborted. When this happens, the + // interrupt reason in |abort_reason_| will be used instead of USER_CANCELED + // which is vague. + DownloadInterruptReason abort_reason_; // Each successful OnWillRead will yield a buffer of this size. static const int kReadBufSize = 32768; // bytes diff --git a/chromium/content/browser/download/download_request_handle.cc b/chromium/content/browser/download/download_request_handle.cc index 67dda4f5029..f5979b945ad 100644 --- a/chromium/content/browser/download/download_request_handle.cc +++ b/chromium/content/browser/download/download_request_handle.cc @@ -6,88 +6,39 @@ #include "base/bind.h" #include "base/strings/stringprintf.h" -#include "content/browser/frame_host/navigator.h" -#include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/browser_side_navigation_policy.h" namespace content { DownloadRequestHandleInterface::~DownloadRequestHandleInterface() {} +DownloadRequestHandle::DownloadRequestHandle( + const DownloadRequestHandle& other) = default; + DownloadRequestHandle::~DownloadRequestHandle() {} -DownloadRequestHandle::DownloadRequestHandle() - : child_id_(-1), - render_view_id_(-1), - request_id_(-1), - frame_tree_node_id_(-1) { -} +DownloadRequestHandle::DownloadRequestHandle() {} DownloadRequestHandle::DownloadRequestHandle( const base::WeakPtr& handler, - int child_id, - int render_view_id, - int request_id, - int frame_tree_node_id) - : handler_(handler), - child_id_(child_id), - render_view_id_(render_view_id), - request_id_(request_id), - frame_tree_node_id_(frame_tree_node_id) { + const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter) + : handler_(handler), web_contents_getter_(web_contents_getter) { DCHECK(handler_.get()); } WebContents* DownloadRequestHandle::GetWebContents() const { - // PlzNavigate: if a FrameTreeNodeId was provided, use it to return the - // WebContents. - // TODO(davidben): This logic should be abstracted within the ResourceLoader - // stack. https://crbug.com/376003 - if (IsBrowserSideNavigationEnabled()) { - FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id_); - if (frame_tree_node) { - return WebContentsImpl::FromFrameTreeNode(frame_tree_node); - } - } - - RenderViewHostImpl* render_view_host = - RenderViewHostImpl::FromID(child_id_, render_view_id_); - if (!render_view_host) - return nullptr; - - return render_view_host->GetDelegate()->GetAsWebContents(); + return web_contents_getter_.is_null() ? nullptr : web_contents_getter_.Run(); } DownloadManager* DownloadRequestHandle::GetDownloadManager() const { - // PlzNavigate: if a FrameTreeNodeId was provided, use it to return the - // DownloadManager. - // TODO(davidben): This logic should be abstracted within the ResourceLoader - // stack. https://crbug.com/376003 - if (IsBrowserSideNavigationEnabled()) { - FrameTreeNode* frame_tree_node = - FrameTreeNode::GloballyFindByID(frame_tree_node_id_); - if (frame_tree_node) { - BrowserContext* context = - frame_tree_node->navigator()->GetController()->GetBrowserContext(); - if (context) - return BrowserContext::GetDownloadManager(context); - } - } - - RenderViewHostImpl* rvh = RenderViewHostImpl::FromID( - child_id_, render_view_id_); - if (rvh == NULL) - return NULL; - RenderProcessHost* rph = rvh->GetProcess(); - if (rph == NULL) - return NULL; - BrowserContext* context = rph->GetBrowserContext(); - if (context == NULL) - return NULL; + WebContents* web_contents = GetWebContents(); + if (web_contents == nullptr) + return nullptr; + BrowserContext* context = web_contents->GetBrowserContext(); + if (context == nullptr) + return nullptr; return BrowserContext::GetDownloadManager(context); } @@ -109,15 +60,4 @@ void DownloadRequestHandle::CancelRequest() const { base::Bind(&DownloadResourceHandler::CancelRequest, handler_)); } -std::string DownloadRequestHandle::DebugString() const { - return base::StringPrintf("{" - " child_id = %d" - " render_view_id = %d" - " request_id = %d" - "}", - child_id_, - render_view_id_, - request_id_); -} - } // namespace content diff --git a/chromium/content/browser/download/download_request_handle.h b/chromium/content/browser/download/download_request_handle.h index 5d047c75910..090a25bb5a6 100644 --- a/chromium/content/browser/download/download_request_handle.h +++ b/chromium/content/browser/download/download_request_handle.h @@ -5,12 +5,11 @@ #ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_HANDLE_H_ #define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_HANDLE_H_ -#include - #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" #include "content/browser/download/download_resource_handler.h" #include "content/common/content_export.h" +#include "content/public/browser/resource_request_info.h" namespace content { class DownloadManager; @@ -36,15 +35,13 @@ class CONTENT_EXPORT DownloadRequestHandleInterface { // Cancels the request. virtual void CancelRequest() const = 0; - - // Describes the object. - virtual std::string DebugString() const = 0; }; class CONTENT_EXPORT DownloadRequestHandle : public DownloadRequestHandleInterface { public: + DownloadRequestHandle(const DownloadRequestHandle& other); ~DownloadRequestHandle() override; // Create a null DownloadRequestHandle: getters will return null, and @@ -56,12 +53,9 @@ class CONTENT_EXPORT DownloadRequestHandle // allowing mocking of ResourceDispatcherHost in unit tests. DownloadRequestHandle(); - // Note that |rdh| is required to be non-null. DownloadRequestHandle(const base::WeakPtr& handler, - int child_id, - int render_view_id, - int request_id, - int frame_tree_node_id); + const content::ResourceRequestInfo::WebContentsGetter& + web_contents_getter); // Implement DownloadRequestHandleInterface interface. WebContents* GetWebContents() const override; @@ -69,23 +63,10 @@ class CONTENT_EXPORT DownloadRequestHandle void PauseRequest() const override; void ResumeRequest() const override; void CancelRequest() const override; - std::string DebugString() const override; private: base::WeakPtr handler_; - - // The ID of the child process that started the download. - int child_id_; - - // The ID of the render view that started the download. - int render_view_id_; - - // The ID associated with the request used for the download. - int request_id_; - - // PlzNavigate - // The ID of the FrameTreeNode that started the download. - int frame_tree_node_id_; + content::ResourceRequestInfo::WebContentsGetter web_contents_getter_; }; } // namespace content diff --git a/chromium/content/browser/download/download_resource_handler.cc b/chromium/content/browser/download/download_resource_handler.cc index e06b3095e99..30a7c8cf029 100644 --- a/chromium/content/browser/download/download_resource_handler.cc +++ b/chromium/content/browser/download/download_resource_handler.cc @@ -48,11 +48,11 @@ static void StartOnUIThread( // NULL in unittests or if the page closed right after starting the // download. if (!started_cb.is_null()) - started_cb.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); - - // |stream| gets deleted on non-FILE thread, but it's ok since - // we're not using stream_writer_ yet. + started_cb.Run(nullptr, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); + if (stream) + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, + stream.release()); return; } @@ -83,19 +83,10 @@ void DeleteOnUIThread( } // namespace -DownloadResourceHandler::DownloadResourceHandler( - uint32_t id, - net::URLRequest* request, - const DownloadUrlParameters::OnStartedCallback& started_cb, - scoped_ptr save_info) +DownloadResourceHandler::DownloadResourceHandler(net::URLRequest* request) : ResourceHandler(request), - download_id_(id), - started_cb_(started_cb), tab_info_(new DownloadTabInfo()), - core_(request, - std::move(save_info), - base::Bind(&DownloadResourceHandler::OnCoreReadyToRead, - base::Unretained(this))) { + core_(request, this) { // Do UI thread initialization for tab_info_ asap after // DownloadResourceHandler creation since the tab could be navigated // before StartOnUIThread gets called. This is safe because deletion @@ -104,20 +95,14 @@ DownloadResourceHandler::DownloadResourceHandler( const ResourceRequestInfoImpl* request_info = GetRequestInfo(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&InitializeDownloadTabInfoOnUIThread, - DownloadRequestHandle(AsWeakPtr(), request_info->GetChildID(), - request_info->GetRouteID(), - request_info->GetRequestID(), - request_info->frame_tree_node_id()), - tab_info_.get())); + base::Bind( + &InitializeDownloadTabInfoOnUIThread, + DownloadRequestHandle(AsWeakPtr(), + request_info->GetWebContentsGetterForRequest()), + tab_info_.get())); } DownloadResourceHandler::~DownloadResourceHandler() { - // This won't do anything if the callback was called before. - // If it goes through, it will likely be because OnWillStart() returned - // false somewhere in the chain of resource handlers. - CallStartedCB(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); - if (tab_info_) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -129,46 +114,16 @@ bool DownloadResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - return true; + return core_.OnRequestRedirected(); } // Send the download creation information to the download thread. bool DownloadResourceHandler::OnResponseStarted( ResourceResponse* response, bool* defer) { - scoped_ptr create_info; - scoped_ptr stream_reader; - - core_.OnResponseStarted(&create_info, &stream_reader); - - const ResourceRequestInfoImpl* request_info = GetRequestInfo(); - create_info->download_id = download_id_; - create_info->has_user_gesture = request_info->HasUserGesture(); - create_info->transition_type = request_info->GetPageTransition(); - create_info->request_handle.reset(new DownloadRequestHandle( - AsWeakPtr(), request_info->GetChildID(), request_info->GetRouteID(), - request_info->GetRequestID(), request_info->frame_tree_node_id())); - // The MIME type in ResourceResponse is the product of // MimeTypeResourceHandler. - create_info->mime_type = response->head.mime_type; - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&StartOnUIThread, base::Passed(&create_info), - base::Passed(&tab_info_), base::Passed(&stream_reader), - base::ResetAndReturn(&started_cb_))); - return true; -} - -void DownloadResourceHandler::CallStartedCB( - DownloadInterruptReason interrupt_reason) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (started_cb_.is_null()) - return; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::ResetAndReturn(&started_cb_), - nullptr, interrupt_reason)); + return core_.OnResponseStarted(response->head.mime_type); } bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) { @@ -197,8 +152,7 @@ void DownloadResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, const std::string& security_info, bool* defer) { - DownloadInterruptReason result = core_.OnResponseCompleted(status); - CallStartedCB(result); + core_.OnResponseCompleted(status); } void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -213,7 +167,36 @@ void DownloadResourceHandler::ResumeRequest() { core_.ResumeRequest(); } -void DownloadResourceHandler::OnCoreReadyToRead() { +void DownloadResourceHandler::OnStart( + scoped_ptr create_info, + scoped_ptr stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) { + // If the user cancels the download, then don't call start. Instead ignore the + // download entirely. + if (create_info->result == DOWNLOAD_INTERRUPT_REASON_USER_CANCELED && + create_info->download_id == DownloadItem::kInvalidId) { + if (!callback.is_null()) + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(callback, nullptr, create_info->result)); + return; + } + + const ResourceRequestInfoImpl* request_info = GetRequestInfo(); + create_info->has_user_gesture = request_info->HasUserGesture(); + create_info->transition_type = request_info->GetPageTransition(); + + create_info->request_handle.reset(new DownloadRequestHandle( + AsWeakPtr(), request_info->GetWebContentsGetterForRequest())); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&StartOnUIThread, base::Passed(&create_info), + base::Passed(&tab_info_), base::Passed(&stream_reader), + callback)); +} + +void DownloadResourceHandler::OnReadyToRead() { DCHECK_CURRENTLY_ON(BrowserThread::IO); controller()->Resume(); } diff --git a/chromium/content/browser/download/download_resource_handler.h b/chromium/content/browser/download/download_resource_handler.h index 22a10a8a049..b874b9a43bb 100644 --- a/chromium/content/browser/download/download_resource_handler.h +++ b/chromium/content/browser/download/download_resource_handler.h @@ -33,17 +33,14 @@ struct DownloadCreateInfo; // Forwards data to the download thread. class CONTENT_EXPORT DownloadResourceHandler : public ResourceHandler, + public DownloadRequestCore::Delegate, public base::SupportsWeakPtr { public: struct DownloadTabInfo; // started_cb will be called exactly once on the UI thread. // |id| should be invalid if the id should be automatically assigned. - DownloadResourceHandler( - uint32_t id, - net::URLRequest* request, - const DownloadUrlParameters::OnStartedCallback& started_cb, - scoped_ptr save_info); + DownloadResourceHandler(net::URLRequest* request); bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, @@ -84,18 +81,12 @@ class CONTENT_EXPORT DownloadResourceHandler private: ~DownloadResourceHandler() override; - // Arrange for started_cb_ to be called on the UI thread with the - // below values, nulling out started_cb_. Should only be called - // on the IO thread. - void CallStartedCB(DownloadInterruptReason interrupt_reason); - - void OnCoreReadyToRead(); - - uint32_t download_id_; - - // This is read only on the IO thread, but may only - // be called on the UI thread. - DownloadUrlParameters::OnStartedCallback started_cb_; + // DownloadRequestCore::Delegate + void OnStart( + scoped_ptr download_create_info, + scoped_ptr stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) override; + void OnReadyToRead() override; // Stores information about the download that must be acquired on the UI // thread before StartOnUIThread is called. diff --git a/chromium/content/browser/download/drag_download_file.cc b/chromium/content/browser/download/drag_download_file.cc index afb533a56bf..dc6b89c8b8f 100644 --- a/chromium/content/browser/download/drag_download_file.cc +++ b/chromium/content/browser/download/drag_download_file.cc @@ -97,8 +97,8 @@ class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer { void OnDownloadStarted(DownloadItem* item, DownloadInterruptReason interrupt_reason) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!item) { - DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); + if (!item || item->GetState() != DownloadItem::IN_PROGRESS) { + DCHECK(!item || item->GetLastReason() != DOWNLOAD_INTERRUPT_REASON_NONE); on_completed_loop_->task_runner()->PostTask( FROM_HERE, base::Bind(on_completed_, false)); return; diff --git a/chromium/content/browser/download/mhtml_generation_manager.cc b/chromium/content/browser/download/mhtml_generation_manager.cc index ef9202330d2..623ab53546e 100644 --- a/chromium/content/browser/download/mhtml_generation_manager.cc +++ b/chromium/content/browser/download/mhtml_generation_manager.cc @@ -188,9 +188,8 @@ bool MHTMLGenerationManager::Job::SendToNextRenderFrame() { ipc_params.salt = salt_; ipc_params.digests_of_uris_to_skip = digests_of_already_serialized_uris_; - ipc_params.destination_file = IPC::GetFileHandleForProcess( - browser_file_.GetPlatformFile(), rfh->GetProcess()->GetHandle(), - false); // |close_source_handle|. + ipc_params.destination_file = IPC::GetPlatformFileForTransit( + browser_file_.GetPlatformFile(), false); // |close_source_handle|. ipc_params.frame_routing_id_to_content_id = CreateFrameRoutingIdToContentId(rfh->GetSiteInstance()); diff --git a/chromium/content/browser/download/mock_download_file.h b/chromium/content/browser/download/mock_download_file.h index a0020b686c9..7109c44c157 100644 --- a/chromium/content/browser/download/mock_download_file.h +++ b/chromium/content/browser/download/mock_download_file.h @@ -35,23 +35,24 @@ class MockDownloadFile : public DownloadFile { MOCK_METHOD2(RenameAndUniquify, void(const base::FilePath& full_path, const RenameCompletionCallback& callback)); - MOCK_METHOD2(RenameAndAnnotate, + MOCK_METHOD5(RenameAndAnnotate, void(const base::FilePath& full_path, + const std::string& client_guid, + const GURL& source_url, + const GURL& referrer_url, const RenameCompletionCallback& callback)); MOCK_METHOD0(Detach, void()); MOCK_METHOD0(Cancel, void()); MOCK_METHOD0(Finish, void()); - MOCK_CONST_METHOD0(FullPath, base::FilePath()); + MOCK_CONST_METHOD0(FullPath, const base::FilePath&()); MOCK_CONST_METHOD0(InProgress, bool()); MOCK_CONST_METHOD0(BytesSoFar, int64_t()); MOCK_CONST_METHOD0(CurrentSpeed, int64_t()); MOCK_METHOD1(GetHash, bool(std::string* hash)); - MOCK_METHOD0(GetHashState, std::string()); MOCK_METHOD0(SendUpdate, void()); MOCK_CONST_METHOD0(Id, int()); MOCK_METHOD0(GetDownloadManager, DownloadManager*()); MOCK_CONST_METHOD0(DebugString, std::string()); - MOCK_METHOD1(SetClientGuid, void(const std::string&)); }; } // namespace content diff --git a/chromium/content/browser/download/save_file.cc b/chromium/content/browser/download/save_file.cc index 5085e477c15..2eab52b0451 100644 --- a/chromium/content/browser/download/save_file.cc +++ b/chromium/content/browser/download/save_file.cc @@ -14,15 +14,7 @@ namespace content { // Unfortunately, as it is, constructors of SaveFile don't always // have access to the SavePackage at this point. SaveFile::SaveFile(const SaveFileCreateInfo* info, bool calculate_hash) - : file_(base::FilePath(), - info->url, - GURL(), - 0, - calculate_hash, - std::string(), - base::File(), - net::BoundNetLog()), - info_(info) { + : file_(net::BoundNetLog()), info_(info) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK(info); @@ -34,7 +26,12 @@ SaveFile::~SaveFile() { } DownloadInterruptReason SaveFile::Initialize() { - return file_.Initialize(base::FilePath()); + return file_.Initialize(base::FilePath(), + base::FilePath(), + base::File(), + 0, + std::string(), + scoped_ptr()); } DownloadInterruptReason SaveFile::AppendDataToFile(const char* data, @@ -61,7 +58,7 @@ void SaveFile::Finish() { void SaveFile::AnnotateWithSourceInformation() { // TODO(gbillock): If this method is called, it should set the // file_.SetClientGuid() method first. - file_.AnnotateWithSourceInformation(); + NOTREACHED(); } base::FilePath SaveFile::FullPath() const { @@ -76,10 +73,6 @@ int64_t SaveFile::BytesSoFar() const { return file_.bytes_so_far(); } -bool SaveFile::GetHash(std::string* hash) { - return file_.GetHash(hash); -} - std::string SaveFile::DebugString() const { return file_.DebugString(); } diff --git a/chromium/content/browser/download/save_file.h b/chromium/content/browser/download/save_file.h index 1dc82170ad0..69add3279da 100644 --- a/chromium/content/browser/download/save_file.h +++ b/chromium/content/browser/download/save_file.h @@ -38,7 +38,6 @@ class SaveFile { base::FilePath FullPath() const; bool InProgress() const; int64_t BytesSoFar() const; - bool GetHash(std::string* hash); std::string DebugString() const; // Accessors. diff --git a/chromium/content/browser/download/save_file_manager.cc b/chromium/content/browser/download/save_file_manager.cc index cf2e59ce3f1..527a7a18fbb 100644 --- a/chromium/content/browser/download/save_file_manager.cc +++ b/chromium/content/browser/download/save_file_manager.cc @@ -74,6 +74,10 @@ void SaveFileManager::SaveURL(SaveItemId save_item_id, SavePackage* save_package) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + // Insert started saving job to tracking list. + DCHECK(packages_.find(save_item_id) == packages_.end()); + packages_[save_item_id] = save_package; + // Register a saving job. if (save_source == SaveFileCreateInfo::SAVE_FILE_FROM_NET) { DCHECK(url.is_valid()); @@ -201,19 +205,27 @@ void SaveFileManager::SaveFinished(SaveItemId save_item_id, << " save_package_id = " << save_package_id << " is_success = " << is_success; DCHECK_CURRENTLY_ON(BrowserThread::FILE); + + int64_t bytes_so_far = 0; SaveFile* save_file = LookupSaveFile(save_item_id); if (save_file != nullptr) { DCHECK(save_file->InProgress()); DVLOG(20) << " " << __FUNCTION__ << "()" << " save_file = " << save_file->DebugString(); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&SaveFileManager::OnSaveFinished, this, save_item_id, - save_file->BytesSoFar(), is_success)); - + bytes_so_far = save_file->BytesSoFar(); save_file->Finish(); save_file->Detach(); + } else { + // We got called before StartSave - this should only happen if + // ResourceHandler failed before it got a chance to parse headers + // and metadata. + DCHECK(!is_success); } + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&SaveFileManager::OnSaveFinished, this, save_item_id, + bytes_so_far, is_success)); } // Notifications sent from the file thread and run on the UI thread. @@ -228,10 +240,6 @@ void SaveFileManager::OnStartSave(const SaveFileCreateInfo& info) { return; } - // Insert started saving job to tracking list. - DCHECK(packages_.find(info.save_item_id) == packages_.end()); - packages_[info.save_item_id] = save_package; - // Forward this message to SavePackage. save_package->StartSave(&info); } diff --git a/chromium/content/browser/download/save_file_manager.h b/chromium/content/browser/download/save_file_manager.h index 3f36206f43c..e99c2a23b8a 100644 --- a/chromium/content/browser/download/save_file_manager.h +++ b/chromium/content/browser/download/save_file_manager.h @@ -60,8 +60,8 @@ #include #include +#include -#include "base/containers/hash_tables.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/browser/download/save_types.h" @@ -90,8 +90,10 @@ class SaveFileManager : public base::RefCountedThreadSafe { // Lifetime management. CONTENT_EXPORT void Shutdown(); - // Save the specified URL. Called on the UI thread and forwarded to the - // ResourceDispatcherHostImpl on the IO thread. + // Save the specified URL. Caller has to guarantee that |save_package| will + // be alive until the call to RemoveSaveFile. Called on the UI thread (and in + // case of network downloads forwarded to the ResourceDispatcherHostImpl on + // the IO thread). void SaveURL(SaveItemId save_item_id, const GURL& url, const Referrer& referrer, @@ -204,12 +206,14 @@ class SaveFileManager : public base::RefCountedThreadSafe { void ExecuteCancelSaveRequest(int render_process_id, int request_id); // A map from save_item_id into SaveFiles. - typedef base::hash_map SaveFileMap; + using SaveFileMap = + std::unordered_map; SaveFileMap save_file_map_; // Tracks which SavePackage to send data to, called only on UI thread. // SavePackageMap maps save item ids to their SavePackage. - typedef base::hash_map SavePackageMap; + using SavePackageMap = + std::unordered_map; SavePackageMap packages_; DISALLOW_COPY_AND_ASSIGN(SaveFileManager); diff --git a/chromium/content/browser/download/save_file_resource_handler.cc b/chromium/content/browser/download/save_file_resource_handler.cc index faf5605710a..7cba445aad5 100644 --- a/chromium/content/browser/download/save_file_resource_handler.cc +++ b/chromium/content/browser/download/save_file_resource_handler.cc @@ -85,7 +85,7 @@ bool SaveFileResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&SaveFileManager::UpdateSaveProgress, save_manager_, - save_item_id_, buffer, bytes_read)); + save_item_id_, base::RetainedRef(buffer), bytes_read)); return true; } diff --git a/chromium/content/browser/download/save_item.cc b/chromium/content/browser/download/save_item.cc index edea2632804..5fa262778e8 100644 --- a/chromium/content/browser/download/save_item.cc +++ b/chromium/content/browser/download/save_item.cc @@ -27,10 +27,12 @@ SaveItemId GetNextSaveItemId() { SaveItem::SaveItem(const GURL& url, const Referrer& referrer, SavePackage* package, - SaveFileCreateInfo::SaveFileSource save_source) + SaveFileCreateInfo::SaveFileSource save_source, + int frame_tree_node_id) : save_item_id_(GetNextSaveItemId()), url_(url), referrer_(referrer), + frame_tree_node_id_(frame_tree_node_id), total_bytes_(0), received_bytes_(0), state_(WAIT_START), diff --git a/chromium/content/browser/download/save_item.h b/chromium/content/browser/download/save_item.h index b0e655da71e..ef463953bf7 100644 --- a/chromium/content/browser/download/save_item.h +++ b/chromium/content/browser/download/save_item.h @@ -30,7 +30,8 @@ class SaveItem { SaveItem(const GURL& url, const Referrer& referrer, SavePackage* package, - SaveFileCreateInfo::SaveFileSource save_source); + SaveFileCreateInfo::SaveFileSource save_source, + int frame_tree_node_id); ~SaveItem(); @@ -61,6 +62,7 @@ class SaveItem { const base::FilePath& file_name() const { return file_name_; } const GURL& url() const { return url_; } const Referrer& referrer() const { return referrer_; } + int frame_tree_node_id() const { return frame_tree_node_id_; } int64_t total_bytes() const { return total_bytes_; } int64_t received_bytes() const { return received_bytes_; } bool has_final_name() const { return has_final_name_; } @@ -87,6 +89,10 @@ class SaveItem { GURL url_; Referrer referrer_; + // Frame tree node id, if this save item represents a frame + // (otherwise FrameTreeNode::kFrameTreeNodeInvalidID). + int frame_tree_node_id_; + // Total bytes expected. int64_t total_bytes_; diff --git a/chromium/content/browser/download/save_package.cc b/chromium/content/browser/download/save_package.cc index 5b7d6a6ead1..7c989856f6b 100644 --- a/chromium/content/browser/download/save_package.cc +++ b/chromium/content/browser/download/save_package.cc @@ -131,9 +131,6 @@ class SavePackageRequestHandle : public DownloadRequestHandleInterface { void PauseRequest() const override {} void ResumeRequest() const override {} void CancelRequest() const override {} - std::string DebugString() const override { - return "SavePackage DownloadRequestHandle"; - } private: base::WeakPtr save_package_; @@ -354,10 +351,8 @@ void SavePackage::InitWithDownloadItem( SaveFileCreateInfo::SaveFileSource save_source = page_url_.SchemeIsFile() ? SaveFileCreateInfo::SAVE_FILE_FROM_FILE : SaveFileCreateInfo::SAVE_FILE_FROM_NET; - SaveItem* save_item = new SaveItem(page_url_, - Referrer(), - this, - save_source); + SaveItem* save_item = new SaveItem(page_url_, Referrer(), this, save_source, + FrameTreeNode::kFrameTreeNodeInvalidId); // Add this item to waiting list. waiting_item_queue_.push_back(save_item); all_save_items_count_ = 1; @@ -378,12 +373,10 @@ void SavePackage::OnMHTMLGenerated(int64_t size) { // TODO(rdsmith/benjhayden): Integrate canceling on DownloadItem // with SavePackage flow. if (download_->GetState() == DownloadItem::IN_PROGRESS) { - download_->SetTotalBytes(size); - download_->DestinationUpdate(size, 0, std::string()); // Must call OnAllDataSaved here in order for // GDataDownloadObserver::ShouldUpload() to return true. // ShouldCompleteDownload() may depend on the gdata uploader to finish. - download_->OnAllDataSaved(DownloadItem::kEmptyFileHash); + download_->OnAllDataSaved(size, scoped_ptr()); } if (!download_manager_->GetDelegate()) { @@ -555,7 +548,7 @@ bool SavePackage::GenerateFileName(const std::string& disposition, // We have received a message from SaveFileManager about a new saving job. We // find a SaveItem and store it in our in_progress list. void SavePackage::StartSave(const SaveFileCreateInfo* info) { - DCHECK(info && !info->url.is_empty()); + DCHECK(info); SaveItemIdMap::iterator it = in_progress_items_.find(info->save_item_id); if (it == in_progress_items_.end()) { @@ -788,9 +781,9 @@ void SavePackage::Finish() { // with SavePackage flow. if (download_->GetState() == DownloadItem::IN_PROGRESS) { if (save_type_ != SAVE_PAGE_TYPE_AS_MHTML) { - download_->DestinationUpdate( - all_save_items_count_, CurrentSpeed(), std::string()); - download_->OnAllDataSaved(DownloadItem::kEmptyFileHash); + download_->DestinationUpdate(all_save_items_count_, CurrentSpeed()); + download_->OnAllDataSaved(all_save_items_count_, + scoped_ptr()); } download_->MarkAsComplete(); } @@ -821,8 +814,7 @@ void SavePackage::SaveFinished(SaveItemId save_item_id, // TODO(rdsmith/benjhayden): Integrate canceling on DownloadItem // with SavePackage flow. if (download_ && (download_->GetState() == DownloadItem::IN_PROGRESS)) { - download_->DestinationUpdate( - completed_count(), CurrentSpeed(), std::string()); + download_->DestinationUpdate(completed_count(), CurrentSpeed()); } if (save_item->save_source() == SaveFileCreateInfo::SAVE_FILE_FROM_DOM && @@ -974,18 +966,28 @@ void SavePackage::GetSerializedHtmlWithLocalLinks() { if (successful_started_items_count != in_process_count()) return; - // Ask all frames for their serialized data. + // Try to serialize all the frames gathered during GetSavableResourceLinks. DCHECK_EQ(0, number_of_frames_pending_response_); FrameTree* frame_tree = static_cast(web_contents()->GetMainFrame()) ->frame_tree_node()->frame_tree(); for (const auto& item : frame_tree_node_id_to_save_item_) { - DCHECK(item.second); // SaveItem* != nullptr. int frame_tree_node_id = item.first; + SaveItem* save_item = item.second; + DCHECK(save_item); + FrameTreeNode* frame_tree_node = frame_tree->FindByID(frame_tree_node_id); - if (frame_tree_node) { + if (frame_tree_node && + frame_tree_node->current_frame_host()->IsRenderFrameLive()) { + // Ask the frame for HTML to be written to the associated SaveItem. GetSerializedHtmlWithLocalLinksForFrame(frame_tree_node); number_of_frames_pending_response_++; + } else { + // Notify SaveFileManager about the failure to save this SaveItem. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&SaveFileManager::SaveFinished, file_manager_, + save_item->id(), id(), false)); } } if (number_of_frames_pending_response_ == 0) { @@ -999,30 +1001,59 @@ void SavePackage::GetSerializedHtmlWithLocalLinksForFrame( FrameTreeNode* target_tree_node) { DCHECK(target_tree_node); int target_frame_tree_node_id = target_tree_node->frame_tree_node_id(); + RenderFrameHostImpl* target = target_tree_node->current_frame_host(); // Collect all saved success items. // SECURITY NOTE: We don't send *all* urls / local paths, but only // those that the given frame had access to already (because it contained // the savable resources / subframes associated with save items). std::map url_to_local_path; + std::map routing_id_to_local_path; auto it = frame_tree_node_id_to_contained_save_items_.find( target_frame_tree_node_id); if (it != frame_tree_node_id_to_contained_save_items_.end()) { for (SaveItem* save_item : it->second) { - DCHECK(save_item->has_final_name()); + // Skip items that failed to save. + if (!save_item->has_final_name()) { + DCHECK_EQ(SaveItem::SaveState::COMPLETE, save_item->state()); + DCHECK(!save_item->success()); + continue; + } + + // Calculate the relative path for referring to the |save_item|. base::FilePath local_path(base::FilePath::kCurrentDirectory); if (target_tree_node->IsMainFrame()) { local_path = local_path.Append(saved_main_directory_path_.BaseName()); } local_path = local_path.Append(save_item->file_name()); - url_to_local_path[save_item->url()] = local_path; + + // Insert the link into |url_to_local_path| or |routing_id_to_local_path|. + if (save_item->save_source() != SaveFileCreateInfo::SAVE_FILE_FROM_DOM) { + DCHECK_EQ(FrameTreeNode::kFrameTreeNodeInvalidId, + save_item->frame_tree_node_id()); + url_to_local_path[save_item->url()] = local_path; + } else { + FrameTreeNode* save_item_frame_tree_node = + target_tree_node->frame_tree()->FindByID( + save_item->frame_tree_node_id()); + if (!save_item_frame_tree_node) { + // crbug.com/541354: Raciness when saving a dynamically changing page. + continue; + } + + int routing_id = + save_item_frame_tree_node->render_manager() + ->GetRoutingIdForSiteInstance(target->GetSiteInstance()); + DCHECK_NE(MSG_ROUTING_NONE, routing_id); + + routing_id_to_local_path[routing_id] = local_path; + } } } // Ask target frame to serialize itself. - RenderFrameHostImpl* target = target_tree_node->current_frame_host(); target->Send(new FrameMsg_GetSerializedHtmlWithLocalLinks( - target->GetRoutingID(), url_to_local_path)); + target->GetRoutingID(), url_to_local_path, routing_id_to_local_path)); } // Process the serialized HTML content data of a specified frame @@ -1069,7 +1100,8 @@ void SavePackage::OnSerializedHtmlWithLocalLinksResponse( BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&SaveFileManager::UpdateSaveProgress, file_manager_, - save_item->id(), new_data, static_cast(data.size()))); + save_item->id(), base::RetainedRef(new_data), + static_cast(data.size()))); } // Current frame is completed saving, call finish in file thread. @@ -1150,14 +1182,14 @@ void SavePackage::OnSavableResourceLinksResponse( SaveItem* SavePackage::CreatePendingSaveItem( int container_frame_tree_node_id, + int save_item_frame_tree_node_id, const GURL& url, const Referrer& referrer, SaveFileCreateInfo::SaveFileSource save_source) { - DCHECK(url.is_valid()); // |url| should be validated by the callers. - SaveItem* save_item; Referrer sanitized_referrer = Referrer::SanitizeForRequest(url, referrer); - save_item = new SaveItem(url, sanitized_referrer, this, save_source); + save_item = new SaveItem(url, sanitized_referrer, this, save_source, + save_item_frame_tree_node_id); waiting_item_queue_.push_back(save_item); frame_tree_node_id_to_contained_save_items_[container_frame_tree_node_id] @@ -1167,6 +1199,7 @@ SaveItem* SavePackage::CreatePendingSaveItem( SaveItem* SavePackage::CreatePendingSaveItemDeduplicatingByUrl( int container_frame_tree_node_id, + int save_item_frame_tree_node_id, const GURL& url, const Referrer& referrer, SaveFileCreateInfo::SaveFileSource save_source) { @@ -1182,7 +1215,8 @@ SaveItem* SavePackage::CreatePendingSaveItemDeduplicatingByUrl( frame_tree_node_id_to_contained_save_items_[container_frame_tree_node_id] .push_back(save_item); } else { - save_item = CreatePendingSaveItem(container_frame_tree_node_id, url, + save_item = CreatePendingSaveItem(container_frame_tree_node_id, + save_item_frame_tree_node_id, url, referrer, save_source); url_to_save_item_[url] = save_item; } @@ -1199,19 +1233,17 @@ void SavePackage::EnqueueSavableResource(int container_frame_tree_node_id, SaveFileCreateInfo::SaveFileSource save_source = url.SchemeIsFile() ? SaveFileCreateInfo::SAVE_FILE_FROM_FILE : SaveFileCreateInfo::SAVE_FILE_FROM_NET; - CreatePendingSaveItemDeduplicatingByUrl(container_frame_tree_node_id, url, - referrer, save_source); + CreatePendingSaveItemDeduplicatingByUrl( + container_frame_tree_node_id, FrameTreeNode::kFrameTreeNodeInvalidId, url, + referrer, save_source); } void SavePackage::EnqueueFrame(int container_frame_tree_node_id, int frame_tree_node_id, const GURL& frame_original_url) { - if (!frame_original_url.is_valid()) - return; - - SaveItem* save_item = - CreatePendingSaveItem(container_frame_tree_node_id, frame_original_url, - Referrer(), SaveFileCreateInfo::SAVE_FILE_FROM_DOM); + SaveItem* save_item = CreatePendingSaveItem( + container_frame_tree_node_id, frame_tree_node_id, frame_original_url, + Referrer(), SaveFileCreateInfo::SAVE_FILE_FROM_DOM); DCHECK(save_item); frame_tree_node_id_to_save_item_[frame_tree_node_id] = save_item; } @@ -1261,8 +1293,7 @@ void SavePackage::CompleteSavableResourceLinksResponse() { base::FilePath SavePackage::GetSuggestedNameForSaveAs( bool can_save_as_complete, - const std::string& contents_mime_type, - const std::string& accept_langs) { + const std::string& contents_mime_type) { base::FilePath name_with_proper_ext = base::FilePath::FromUTF16Unsafe(title_); // If the page's title matches its URL, use the URL. Try to use the last path @@ -1274,7 +1305,7 @@ base::FilePath SavePackage::GetSuggestedNameForSaveAs( // back to a URL, and if it matches the original page URL, we know the page // had no title (or had a title equal to its URL, which is fine to treat // similarly). - if (title_ == url_formatter::FormatUrl(page_url_, accept_langs)) { + if (title_ == url_formatter::FormatUrl(page_url_)) { std::string url_path; if (!page_url_.SchemeIs(url::kDataScheme)) { std::vector url_parts = base::SplitString( @@ -1374,23 +1405,17 @@ void SavePackage::GetSaveInfo() { &download_save_dir, &skip_dir_check); } std::string mime_type = web_contents()->GetContentsMimeType(); - std::string accept_languages = - GetContentClient()->browser()->GetAcceptLangs( - web_contents()->GetBrowserContext()); - BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&SavePackage::CreateDirectoryOnFileThread, this, - website_save_dir, download_save_dir, skip_dir_check, - mime_type, accept_languages)); + website_save_dir, download_save_dir, skip_dir_check, mime_type)); } void SavePackage::CreateDirectoryOnFileThread( const base::FilePath& website_save_dir, const base::FilePath& download_save_dir, bool skip_dir_check, - const std::string& mime_type, - const std::string& accept_langs) { + const std::string& mime_type) { base::FilePath save_dir; // If the default html/websites save folder doesn't exist... // We skip the directory check for gdata directories on ChromeOS. @@ -1408,7 +1433,7 @@ void SavePackage::CreateDirectoryOnFileThread( bool can_save_as_complete = CanSaveAsComplete(mime_type); base::FilePath suggested_filename = GetSuggestedNameForSaveAs( - can_save_as_complete, mime_type, accept_langs); + can_save_as_complete, mime_type); base::FilePath::StringType pure_file_name = suggested_filename.RemoveExtension().BaseName().value(); base::FilePath::StringType file_name_ext = suggested_filename.Extension(); diff --git a/chromium/content/browser/download/save_package.h b/chromium/content/browser/download/save_package.h index eabf65717a0..5b0e958c3a0 100644 --- a/chromium/content/browser/download/save_package.h +++ b/chromium/content/browser/download/save_package.h @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "base/containers/hash_tables.h" #include "base/files/file_path.h" #include "base/gtest_prod_util.h" #include "base/macros.h" @@ -206,6 +206,7 @@ class CONTENT_EXPORT SavePackage // Helper for finding or creating a SaveItem with the given parameters. SaveItem* CreatePendingSaveItem( int container_frame_tree_node_id, + int save_item_frame_tree_node_id, const GURL& url, const Referrer& referrer, SaveFileCreateInfo::SaveFileSource save_source); @@ -214,6 +215,7 @@ class CONTENT_EXPORT SavePackage // creating a SaveItem with the given parameters. SaveItem* CreatePendingSaveItemDeduplicatingByUrl( int container_frame_tree_node_id, + int save_item_frame_tree_node_id, const GURL& url, const Referrer& referrer, SaveFileCreateInfo::SaveFileSource save_source); @@ -263,8 +265,7 @@ class CONTENT_EXPORT SavePackage void CreateDirectoryOnFileThread(const base::FilePath& website_save_dir, const base::FilePath& download_save_dir, bool skip_dir_check, - const std::string& mime_type, - const std::string& accept_langs); + const std::string& mime_type); void ContinueGetSaveInfo(const base::FilePath& suggested_path, bool can_save_as_complete); void OnPathPicked( @@ -273,7 +274,8 @@ class CONTENT_EXPORT SavePackage const SavePackageDownloadCreatedCallback& cb); // Map from SaveItem::id() (aka save_item_id) into a SaveItem. - typedef base::hash_map SaveItemIdMap; + using SaveItemIdMap = + std::unordered_map; // in_progress_items_ is map of all saving job in in-progress state. SaveItemIdMap in_progress_items_; // saved_failed_items_ is map of all saving job which are failed. @@ -302,8 +304,7 @@ class CONTENT_EXPORT SavePackage // suggested name is determined by the web document's title. base::FilePath GetSuggestedNameForSaveAs( bool can_save_as_complete, - const std::string& contents_mime_type, - const std::string& accept_langs); + const std::string& contents_mime_type); // Ensures that the file name has a proper extension for HTML by adding ".htm" // if necessary. @@ -319,7 +320,7 @@ class CONTENT_EXPORT SavePackage static const base::FilePath::CharType* ExtensionForMimeType( const std::string& contents_mime_type); - typedef std::deque SaveItemQueue; + using SaveItemQueue = std::deque; // A queue for items we are about to start saving. SaveItemQueue waiting_item_queue_; @@ -333,21 +334,22 @@ class CONTENT_EXPORT SavePackage // OnSerializedHtmlWithLocalLinksResponse) to the right SaveItem. // Note that |frame_tree_node_id_to_save_item_| does NOT own SaveItems - they // remain owned by waiting_item_queue_, in_progress_items_, etc. - base::hash_map frame_tree_node_id_to_save_item_; + std::unordered_map frame_tree_node_id_to_save_item_; // Used to limit which local paths get exposed to which frames // (i.e. to prevent information disclosure to oop frames). // Note that |frame_tree_node_id_to_contained_save_items_| does NOT own // SaveItems - they remain owned by waiting_item_queue_, in_progress_items_, // etc. - base::hash_map> + std::unordered_map> frame_tree_node_id_to_contained_save_items_; // Number of frames that we still need to get a response from. int number_of_frames_pending_response_; // saved_success_items_ is map of all saving job which are successfully saved. - base::hash_map saved_success_items_; + std::unordered_map + saved_success_items_; // Non-owning pointer for handling file writing on the file thread. SaveFileManager* file_manager_; @@ -392,7 +394,8 @@ class CONTENT_EXPORT SavePackage // This set is used to eliminate duplicated file names in saving directory. FileNameSet file_name_set_; - typedef base::hash_map FileNameCountMap; + using FileNameCountMap = + std::unordered_map; // This map is used to track serial number for specified filename. FileNameCountMap file_name_count_map_; diff --git a/chromium/content/browser/download/save_package_unittest.cc b/chromium/content/browser/download/save_package_unittest.cc index fc89b6d1806..c426772a820 100644 --- a/chromium/content/browser/download/save_package_unittest.cc +++ b/chromium/content/browser/download/save_package_unittest.cc @@ -406,8 +406,7 @@ TEST_F(SavePackageTest, MAYBE_TestSuggestedSaveNames) { save_package->title_ = kSuggestedSaveNames[i].page_title; base::FilePath save_name = save_package->GetSuggestedNameForSaveAs( - kSuggestedSaveNames[i].ensure_html_extension, - std::string(), std::string()); + kSuggestedSaveNames[i].ensure_html_extension, std::string()); EXPECT_EQ(kSuggestedSaveNames[i].expected_name, save_name.value()) << "Test case " << i; } diff --git a/chromium/content/browser/download/save_types.cc b/chromium/content/browser/download/save_types.cc index 9c4972a8854..0dd760562c1 100644 --- a/chromium/content/browser/download/save_types.cc +++ b/chromium/content/browser/download/save_types.cc @@ -45,6 +45,9 @@ SaveFileCreateInfo::SaveFileCreateInfo(const GURL& url, total_bytes(total_bytes), save_source(SaveFileCreateInfo::SAVE_FILE_FROM_NET) {} +SaveFileCreateInfo::SaveFileCreateInfo(const SaveFileCreateInfo& other) = + default; + SaveFileCreateInfo::~SaveFileCreateInfo() {} } // namespace content diff --git a/chromium/content/browser/download/save_types.h b/chromium/content/browser/download/save_types.h index 83d890b8dc9..9d61a9a70a0 100644 --- a/chromium/content/browser/download/save_types.h +++ b/chromium/content/browser/download/save_types.h @@ -13,16 +13,16 @@ #include #include "base/files/file_path.h" -#include "content/common/id_type.h" +#include "gpu/command_buffer/common/id_type.h" #include "url/gurl.h" namespace content { class SavePackage; -using SavePackageId = IdType32; +using SavePackageId = gpu::IdType32; class SaveItem; -using SaveItemId = IdType32; +using SaveItemId = gpu::IdType32; // Map from save_item_id into final file path. using FinalNamesMap = std::map; @@ -63,6 +63,8 @@ struct SaveFileCreateInfo { const std::string& content_disposition, int64_t total_bytes); + SaveFileCreateInfo(const SaveFileCreateInfo& other); + ~SaveFileCreateInfo(); // SaveItem fields. diff --git a/chromium/content/browser/download/url_downloader.cc b/chromium/content/browser/download/url_downloader.cc index b178a1d4b26..274de905d8c 100644 --- a/chromium/content/browser/download/url_downloader.cc +++ b/chromium/content/browser/download/url_downloader.cc @@ -60,7 +60,6 @@ class UrlDownloader::RequestHandle : public DownloadRequestHandleInterface { downloader_task_runner_->PostTask( FROM_HERE, base::Bind(&UrlDownloader::CancelRequest, downloader_)); } - std::string DebugString() const override { return std::string(); } private: base::WeakPtr downloader_; @@ -74,62 +73,32 @@ class UrlDownloader::RequestHandle : public DownloadRequestHandleInterface { scoped_ptr UrlDownloader::BeginDownload( base::WeakPtr download_manager, scoped_ptr request, - const Referrer& referrer, - bool prefer_cache, - scoped_ptr save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& started_callback) { + const Referrer& referrer) { if (!referrer.url.is_valid()) request->SetReferrer(std::string()); else request->SetReferrer(referrer.url.spec()); - int extra_load_flags = net::LOAD_NORMAL; - if (prefer_cache) { - // If there is upload data attached, only retrieve from cache because there - // is no current mechanism to prompt the user for their consent for a - // re-post. For GETs, try to retrieve data from the cache and skip - // validating the entry if present. - if (request->get_upload() != NULL) - extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; - else - extra_load_flags |= net::LOAD_PREFERRING_CACHE; - } else { - extra_load_flags |= net::LOAD_DISABLE_CACHE; - } - request->SetLoadFlags(request->load_flags() | extra_load_flags); - if (request->url().SchemeIs(url::kBlobScheme)) return nullptr; // From this point forward, the |UrlDownloader| is responsible for // |started_callback|. scoped_ptr downloader( - new UrlDownloader(std::move(request), download_manager, - std::move(save_info), download_id, started_callback)); + new UrlDownloader(std::move(request), download_manager)); downloader->Start(); return downloader; } -UrlDownloader::UrlDownloader( - scoped_ptr request, - base::WeakPtr manager, - scoped_ptr save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& on_started_callback) +UrlDownloader::UrlDownloader(scoped_ptr request, + base::WeakPtr manager) : request_(std::move(request)), manager_(manager), - download_id_(download_id), - on_started_callback_(on_started_callback), - handler_( - request_.get(), - std::move(save_info), - base::Bind(&UrlDownloader::ResumeReading, base::Unretained(this))), + core_(request_.get(), this), weak_ptr_factory_(this) {} UrlDownloader::~UrlDownloader() { - CallStartedCallbackOnFailure(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); } void UrlDownloader::Start() { @@ -146,7 +115,15 @@ void UrlDownloader::OnReceivedRedirect(net::URLRequest* request, const net::RedirectInfo& redirect_info, bool* defer_redirect) { DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); - request_->CancelWithError(net::ERR_ABORTED); + + // We are going to block redirects even if DownloadRequestCore allows it. No + // redirects are expected for download requests that are made without a + // renderer, which are currently exclusively resumption requests. Since there + // is no security policy being applied here, it's safer to block redirects and + // revisit if some previously unknown legitimate use case arises for redirects + // while resuming. + core_.OnWillAbort(DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE); + request_->CancelWithError(net::ERR_UNSAFE_REDIRECT); } void UrlDownloader::OnResponseStarted(net::URLRequest* request) { @@ -157,22 +134,7 @@ void UrlDownloader::OnResponseStarted(net::URLRequest* request) { return; } - scoped_ptr create_info; - scoped_ptr stream_reader; - - handler_.OnResponseStarted(&create_info, &stream_reader); - - create_info->download_id = download_id_; - create_info->request_handle.reset( - new RequestHandle(weak_ptr_factory_.GetWeakPtr(), manager_, - base::SequencedTaskRunnerHandle::Get())); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManagerImpl::StartDownload, manager_, - base::Passed(&create_info), base::Passed(&stream_reader), - base::ResetAndReturn(&on_started_callback_))); - - if (request_->status().is_success()) + if (core_.OnResponseStarted(std::string())) StartReading(false); // Read the first chunk. else ResponseCompleted(); @@ -186,7 +148,7 @@ void UrlDownloader::StartReading(bool is_continuation) { // doesn't use the buffer. scoped_refptr buf; int buf_size; - if (!handler_.OnWillRead(&buf, &buf_size, -1)) { + if (!core_.OnWillRead(&buf, &buf_size, -1)) { request_->CancelWithError(net::ERR_ABORTED); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&UrlDownloader::ResponseCompleted, @@ -229,7 +191,7 @@ void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { DCHECK(request_->status().is_success()); bool defer = false; - if (!handler_.OnReadCompleted(bytes_read, &defer)) { + if (!core_.OnReadCompleted(bytes_read, &defer)) { request_->CancelWithError(net::ERR_ABORTED); return; } else if (defer) { @@ -251,38 +213,43 @@ void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { void UrlDownloader::ResponseCompleted() { DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); - handler_.OnResponseCompleted(request_->status()); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManagerImpl::RemoveUrlDownloader, manager_, this)); + core_.OnResponseCompleted(request_->status()); + Destroy(); } -void UrlDownloader::ResumeReading() { - if (request_->status().is_success()) { - StartReading(false); // Read the next chunk (OK to complete synchronously). - } else { - ResponseCompleted(); - } +void UrlDownloader::OnStart( + scoped_ptr create_info, + scoped_ptr stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) { + create_info->request_handle.reset( + new RequestHandle(weak_ptr_factory_.GetWeakPtr(), manager_, + base::SequencedTaskRunnerHandle::Get())); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManagerImpl::StartDownload, + manager_, base::Passed(&create_info), + base::Passed(&stream_reader), callback)); } -void UrlDownloader::CallStartedCallbackOnFailure( - DownloadInterruptReason result) { - if (on_started_callback_.is_null()) - return; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(base::ResetAndReturn(&on_started_callback_), nullptr, result)); +void UrlDownloader::OnReadyToRead() { + if (request_->status().is_success()) + StartReading(false); // Read the next chunk (OK to complete synchronously). + else + ResponseCompleted(); } void UrlDownloader::PauseRequest() { - handler_.PauseRequest(); + core_.PauseRequest(); } void UrlDownloader::ResumeRequest() { - handler_.ResumeRequest(); + core_.ResumeRequest(); } void UrlDownloader::CancelRequest() { + Destroy(); +} + +void UrlDownloader::Destroy() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DownloadManagerImpl::RemoveUrlDownloader, manager_, this)); diff --git a/chromium/content/browser/download/url_downloader.h b/chromium/content/browser/download/url_downloader.h index 787feb4c91b..475d97bfbc6 100644 --- a/chromium/content/browser/download/url_downloader.h +++ b/chromium/content/browser/download/url_downloader.h @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "content/browser/download/download_request_core.h" +#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_save_info.h" #include "content/public/browser/download_url_parameters.h" #include "content/public/common/referrer.h" @@ -17,26 +18,26 @@ #include "net/url_request/url_request.h" namespace content { +class ByteStreamReader; +struct DownloadCreateInfo; class DownloadManagerImpl; -class UrlDownloader : public net::URLRequest::Delegate { +class UrlDownloader : public net::URLRequest::Delegate, + public DownloadRequestCore::Delegate { public: - UrlDownloader( - scoped_ptr request, - base::WeakPtr manager, - scoped_ptr save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& on_started_callback); + UrlDownloader(scoped_ptr request, + base::WeakPtr manager); ~UrlDownloader() override; static scoped_ptr BeginDownload( base::WeakPtr download_manager, scoped_ptr request, - const Referrer& referrer, - bool prefer_cache, - scoped_ptr save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& started_callback); + const Referrer& referrer); + + private: + class RequestHandle; + + void Start(); // URLRequest::Delegate: void OnReceivedRedirect(net::URLRequest* request, @@ -48,24 +49,25 @@ class UrlDownloader : public net::URLRequest::Delegate { void StartReading(bool is_continuation); void ResponseCompleted(); - void Start(); - void ResumeReading(); - - void CallStartedCallbackOnFailure(DownloadInterruptReason result); - - private: - class RequestHandle; + // DownloadRequestCore::Delegate + void OnStart( + scoped_ptr download_create_info, + scoped_ptr stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) override; + void OnReadyToRead() override; void PauseRequest(); void ResumeRequest(); void CancelRequest(); + // Called when the UrlDownloader is done with the request. Posts a task to + // remove itself from its download manager, which in turn would cause the + // UrlDownloader to be freed. + void Destroy(); + scoped_ptr request_; base::WeakPtr manager_; - uint32_t download_id_; - DownloadUrlParameters::OnStartedCallback on_started_callback_; - - DownloadRequestCore handler_; + DownloadRequestCore core_; base::WeakPtrFactory weak_ptr_factory_; }; diff --git a/chromium/content/browser/fileapi/blob_reader_unittest.cc b/chromium/content/browser/fileapi/blob_reader_unittest.cc index d16b552d392..1c0ae10a593 100644 --- a/chromium/content/browser/fileapi/blob_reader_unittest.cc +++ b/chromium/content/browser/fileapi/blob_reader_unittest.cc @@ -327,8 +327,7 @@ class BlobReaderTest : public ::testing::Test { void InitializeReader(BlobDataBuilder* builder) { blob_handle_ = builder ? context_.AddFinishedBlob(builder) : nullptr; provider_ = new MockFileStreamReaderProvider(); - scoped_ptr temp_ptr(provider_); - reader_.reset(new BlobReader(blob_handle_.get(), std::move(temp_ptr), + reader_.reset(new BlobReader(blob_handle_.get(), make_scoped_ptr(provider_), message_loop_.task_runner().get())); } @@ -395,8 +394,6 @@ class BlobReaderTest : public ::testing::Test { DISALLOW_COPY_AND_ASSIGN(BlobReaderTest); }; -namespace { - TEST_F(BlobReaderTest, BasicMemory) { BlobDataBuilder b("uuid"); const std::string kData("Hello!!!"); @@ -1113,5 +1110,52 @@ TEST_F(BlobReaderTest, RangeError) { EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error()); } -} // namespace +TEST_F(BlobReaderTest, HandleBeforeAsyncCancel) { + const std::string kUuid("uuid1"); + + context_.CreatePendingBlob(kUuid, "", ""); + blob_handle_ = context_.GetBlobDataFromUUID(kUuid); + provider_ = new MockFileStreamReaderProvider(); + reader_.reset(new BlobReader(blob_handle_.get(), make_scoped_ptr(provider_), + message_loop_.task_runner().get())); + int size_result = -1; + EXPECT_EQ(BlobReader::Status::IO_PENDING, + reader_->CalculateSize(base::Bind(&SetValue, &size_result))); + context_.CancelPendingBlob(kUuid, IPCBlobCreationCancelCode::UNKNOWN); + message_loop_.RunUntilIdle(); + EXPECT_EQ(net::ERR_FAILED, size_result); +} + +TEST_F(BlobReaderTest, ReadFromIncompleteBlob) { + const std::string kUuid("uuid1"); + const std::string kData("Hello!!!"); + const size_t kDataSize = 8ul; + + BlobDataBuilder b(kUuid); + b.AppendData(kData); + context_.CreatePendingBlob(kUuid, "", ""); + blob_handle_ = context_.GetBlobDataFromUUID(kUuid); + provider_ = new MockFileStreamReaderProvider(); + reader_.reset(new BlobReader(blob_handle_.get(), make_scoped_ptr(provider_), + message_loop_.task_runner().get())); + int size_result = -1; + EXPECT_EQ(BlobReader::Status::IO_PENDING, + reader_->CalculateSize(base::Bind(&SetValue, &size_result))); + context_.CompletePendingBlob(b); + message_loop_.RunUntilIdle(); + CheckSizeCalculatedAsynchronously(kDataSize, size_result); + scoped_refptr buffer(new net::IOBuffer(kDataSize)); + + int bytes_read = 0; + int async_bytes_read = 0; + EXPECT_EQ(BlobReader::Status::DONE, + reader_->Read(buffer.get(), kDataSize, &bytes_read, + base::Bind(&SetValue, &async_bytes_read))); + EXPECT_EQ(net::OK, reader_->net_error()); + EXPECT_EQ(kDataSize, static_cast(bytes_read)); + EXPECT_EQ(0, async_bytes_read); + EXPECT_EQ(kData, std::string(buffer->data(), kDataSize)); + EXPECT_EQ(net::OK, size_result); +} + } // namespace storage diff --git a/chromium/content/browser/fileapi/blob_storage_context_unittest.cc b/chromium/content/browser/fileapi/blob_storage_context_unittest.cc index 72df9841708..74e10d0493a 100644 --- a/chromium/content/browser/fileapi/blob_storage_context_unittest.cc +++ b/chromium/content/browser/fileapi/blob_storage_context_unittest.cc @@ -9,32 +9,37 @@ #include #include +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/time/time.h" -#include "content/browser/fileapi/blob_storage_host.h" +#include "content/browser/blob_storage/blob_dispatcher_host.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" +#include "content/public/test/test_browser_context.h" #include "net/base/io_buffer.h" #include "net/base/test_completion_callback.h" #include "net/disk_cache/disk_cache.h" +#include "storage/browser/blob/blob_async_builder_host.h" #include "storage/browser/blob/blob_data_builder.h" #include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/blob/blob_data_item.h" #include "storage/browser/blob/blob_data_snapshot.h" +#include "storage/browser/blob/blob_transport_result.h" +#include "storage/common/blob_storage/blob_item_bytes_request.h" +#include "storage/common/blob_storage/blob_item_bytes_response.h" #include "testing/gtest/include/gtest/gtest.h" -using storage::BlobDataBuilder; -using storage::BlobDataHandle; -using storage::BlobDataItem; -using storage::BlobDataSnapshot; -using storage::BlobStorageContext; -using storage::DataElement; +using RequestMemoryCallback = + storage::BlobAsyncBuilderHost::RequestMemoryCallback; -namespace content { +namespace storage { namespace { +const char kContentType[] = "text/plain"; +const char kContentDisposition[] = "content_disposition"; const int kTestDiskCacheStreamIndex = 0; // Our disk cache tests don't need a real data handle since the tests themselves @@ -74,97 +79,89 @@ disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache, return entry; } -void SetupBasicBlob(BlobStorageHost* host, const std::string& id) { - EXPECT_TRUE(host->StartBuildingBlob(id)); - DataElement item; - item.SetToBytes("1", 1); - EXPECT_TRUE(host->AppendBlobDataItem(id, item)); - EXPECT_TRUE(host->FinishBuildingBlob(id, "text/plain")); - EXPECT_FALSE(host->StartBuildingBlob(id)); -} } // namespace -TEST(BlobStorageContextTest, IncrementDecrementRef) { - BlobStorageContext context; - BlobStorageHost host(&context); +class BlobStorageContextTest : public testing::Test { + protected: + BlobStorageContextTest() {} + ~BlobStorageContextTest() override {} + + scoped_ptr SetupBasicBlob(const std::string& id) { + BlobDataBuilder builder(id); + builder.AppendData("1", 1); + builder.set_content_type("text/plain"); + return context_.AddFinishedBlob(builder); + } + + BlobStorageContext context_; +}; + +TEST_F(BlobStorageContextTest, IncrementDecrementRef) { base::MessageLoop fake_io_message_loop; // Build up a basic blob. const std::string kId("id"); - SetupBasicBlob(&host, kId); + scoped_ptr blob_data_handle = SetupBasicBlob(kId); - // Make sure it's there, finish building implies a ref of one. - scoped_ptr blob_data_handle; - blob_data_handle = context.GetBlobDataFromUUID(kId); + // Do an extra increment to keep it around after we kill the handle. + context_.IncrementBlobRefCount(kId); + context_.IncrementBlobRefCount(kId); + context_.DecrementBlobRefCount(kId); + blob_data_handle = context_.GetBlobDataFromUUID(kId); EXPECT_TRUE(blob_data_handle); blob_data_handle.reset(); base::RunLoop().RunUntilIdle(); - // Make sure its still there after inc/dec. - EXPECT_TRUE(host.IncrementBlobRefCount(kId)); - EXPECT_TRUE(host.DecrementBlobRefCount(kId)); - blob_data_handle = context.GetBlobDataFromUUID(kId); - EXPECT_TRUE(blob_data_handle); - blob_data_handle.reset(); - base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_.registry().HasEntry(kId)); + context_.DecrementBlobRefCount(kId); + EXPECT_FALSE(context_.registry().HasEntry(kId)); // Make sure it goes away in the end. - EXPECT_TRUE(host.DecrementBlobRefCount(kId)); - blob_data_handle = context.GetBlobDataFromUUID(kId); + blob_data_handle = context_.GetBlobDataFromUUID(kId); EXPECT_FALSE(blob_data_handle); - EXPECT_FALSE(host.DecrementBlobRefCount(kId)); - EXPECT_FALSE(host.IncrementBlobRefCount(kId)); } -TEST(BlobStorageContextTest, CancelBuildingBlob) { - BlobStorageContext context; - BlobStorageHost host(&context); +TEST_F(BlobStorageContextTest, OnCancelBuildingBlob) { base::MessageLoop fake_io_message_loop; // Build up a basic blob. const std::string kId("id"); - EXPECT_TRUE(host.StartBuildingBlob(kId)); - DataElement item; - item.SetToBytes("1", 1); - EXPECT_TRUE(host.AppendBlobDataItem(kId, item)); - EXPECT_TRUE(host.CancelBuildingBlob(kId)); - EXPECT_FALSE(host.FinishBuildingBlob(kId, "text/plain")); - EXPECT_TRUE(host.StartBuildingBlob(kId)); + context_.CreatePendingBlob(kId, std::string(kContentType), + std::string(kContentDisposition)); + EXPECT_TRUE(context_.IsBeingBuilt(kId)); + context_.CancelPendingBlob(kId, IPCBlobCreationCancelCode::OUT_OF_MEMORY); + EXPECT_TRUE(context_.registry().HasEntry(kId)); + EXPECT_FALSE(context_.IsBeingBuilt(kId)); + EXPECT_TRUE(context_.IsBroken(kId)); } -TEST(BlobStorageContextTest, BlobDataHandle) { - BlobStorageContext context; - BlobStorageHost host(&context); +TEST_F(BlobStorageContextTest, BlobDataHandle) { base::MessageLoop fake_io_message_loop; // Build up a basic blob. const std::string kId("id"); - SetupBasicBlob(&host, kId); - - // Get a handle to it. - scoped_ptr blob_data_handle = - context.GetBlobDataFromUUID(kId); + scoped_ptr blob_data_handle = SetupBasicBlob(kId); EXPECT_TRUE(blob_data_handle); - // Drop the host's ref to it. - EXPECT_TRUE(host.DecrementBlobRefCount(kId)); - - // Should still be there due to the handle. - scoped_ptr another_handle = - context.GetBlobDataFromUUID(kId); + // Get another handle + scoped_ptr another_handle = context_.GetBlobDataFromUUID(kId); EXPECT_TRUE(another_handle); // Should disappear after dropping both handles. blob_data_handle.reset(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(context_.registry().HasEntry(kId)); + another_handle.reset(); base::RunLoop().RunUntilIdle(); - blob_data_handle = context.GetBlobDataFromUUID(kId); + blob_data_handle = context_.GetBlobDataFromUUID(kId); EXPECT_FALSE(blob_data_handle); } -TEST(BlobStorageContextTest, MemoryUsage) { +TEST_F(BlobStorageContextTest, MemoryUsage) { const std::string kId1("id1"); const std::string kId2("id2"); @@ -181,27 +178,30 @@ TEST(BlobStorageContextTest, MemoryUsage) { builder2.AppendBlob(kId1); builder2.AppendBlob(kId1); - BlobStorageContext context; - EXPECT_EQ(0lu, context.memory_usage()); + EXPECT_EQ(0lu, context_.memory_usage()); scoped_ptr blob_data_handle = - context.AddFinishedBlob(&builder1); - EXPECT_EQ(10lu, context.memory_usage()); + context_.AddFinishedBlob(&builder1); + EXPECT_EQ(10lu, context_.memory_usage()); scoped_ptr blob_data_handle2 = - context.AddFinishedBlob(&builder2); - EXPECT_EQ(10lu, context.memory_usage()); + context_.AddFinishedBlob(&builder2); + EXPECT_EQ(10lu, context_.memory_usage()); + + EXPECT_EQ(2u, context_.registry().blob_count()); blob_data_handle.reset(); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(10lu, context.memory_usage()); + EXPECT_EQ(10lu, context_.memory_usage()); + EXPECT_EQ(1u, context_.registry().blob_count()); blob_data_handle2.reset(); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(0lu, context.memory_usage()); + EXPECT_EQ(0lu, context_.memory_usage()); + EXPECT_EQ(0u, context_.registry().blob_count()); } -TEST(BlobStorageContextTest, AddFinishedBlob) { +TEST_F(BlobStorageContextTest, AddFinishedBlob) { const std::string kId1("id1"); const std::string kId2("id12"); const std::string kId2Prime("id2.prime"); @@ -222,9 +222,9 @@ TEST(BlobStorageContextTest, AddFinishedBlob) { BlobStorageContext context; scoped_ptr blob_data_handle = - context.AddFinishedBlob(&builder1); + context_.AddFinishedBlob(&builder1); scoped_ptr blob_data_handle2 = - context.AddFinishedBlob(&builder2); + context_.AddFinishedBlob(&builder2); ASSERT_TRUE(blob_data_handle); ASSERT_TRUE(blob_data_handle2); @@ -237,7 +237,7 @@ TEST(BlobStorageContextTest, AddFinishedBlob) { base::RunLoop().RunUntilIdle(); - blob_data_handle = context.GetBlobDataFromUUID(kId1); + blob_data_handle = context_.GetBlobDataFromUUID(kId1); EXPECT_FALSE(blob_data_handle); EXPECT_TRUE(blob_data_handle2); data2 = blob_data_handle2->CreateSnapshot(); @@ -248,11 +248,11 @@ TEST(BlobStorageContextTest, AddFinishedBlob) { builder3.AppendBlob(kId2); builder3.AppendBlob(kId2); scoped_ptr blob_data_handle3 = - context.AddFinishedBlob(&builder3); + context_.AddFinishedBlob(&builder3); blob_data_handle2.reset(); base::RunLoop().RunUntilIdle(); - blob_data_handle2 = context.GetBlobDataFromUUID(kId2); + blob_data_handle2 = context_.GetBlobDataFromUUID(kId2); EXPECT_FALSE(blob_data_handle2); EXPECT_TRUE(blob_data_handle3); scoped_ptr data3 = blob_data_handle3->CreateSnapshot(); @@ -270,7 +270,7 @@ TEST(BlobStorageContextTest, AddFinishedBlob) { base::RunLoop().RunUntilIdle(); } -TEST(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { +TEST_F(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { // A value which does not fit in a 4-byte data type. Used to confirm that // large values are supported on 32-bit Chromium builds. Regression test for: // crbug.com/458122. @@ -287,11 +287,10 @@ TEST(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { BlobDataBuilder builder2(kId2); builder2.AppendBlob(kId1, kLargeSize - kBlobLength, kBlobLength); - BlobStorageContext context; scoped_ptr blob_data_handle1 = - context.AddFinishedBlob(&builder1); + context_.AddFinishedBlob(&builder1); scoped_ptr blob_data_handle2 = - context.AddFinishedBlob(&builder2); + context_.AddFinishedBlob(&builder2); ASSERT_TRUE(blob_data_handle1); ASSERT_TRUE(blob_data_handle2); @@ -306,14 +305,13 @@ TEST(BlobStorageContextTest, AddFinishedBlob_LargeOffset) { base::RunLoop().RunUntilIdle(); } -TEST(BlobStorageContextTest, BuildDiskCacheBlob) { +TEST_F(BlobStorageContextTest, BuildDiskCacheBlob) { base::MessageLoop fake_io_message_loop; scoped_refptr data_handle = new EmptyDataHandle(); { - scoped_ptr context(new BlobStorageContext); - BlobStorageHost host(context.get()); + BlobStorageContext context; scoped_ptr cache = CreateInMemoryDiskCache(); ASSERT_TRUE(cache); @@ -333,7 +331,7 @@ TEST(BlobStorageContextTest, BuildDiskCacheBlob) { data_handle, entry.get(), kTestDiskCacheStreamIndex); scoped_ptr blob_data_handle = - context->AddFinishedBlob(&builder); + context.AddFinishedBlob(&builder); scoped_ptr data = blob_data_handle->CreateSnapshot(); EXPECT_EQ(*data, builder); EXPECT_FALSE(data_handle->HasOneRef()) @@ -344,7 +342,7 @@ TEST(BlobStorageContextTest, BuildDiskCacheBlob) { base::RunLoop().RunUntilIdle(); } -TEST(BlobStorageContextTest, CompoundBlobs) { +TEST_F(BlobStorageContextTest, CompoundBlobs) { const std::string kId1("id1"); const std::string kId2("id2"); const std::string kId3("id3"); @@ -390,7 +388,7 @@ TEST(BlobStorageContextTest, CompoundBlobs) { scoped_ptr blob_data_handle; // Test a blob referring to only data and a file. - blob_data_handle = context.AddFinishedBlob(&blob_data1); + blob_data_handle = context_.AddFinishedBlob(&blob_data1); ASSERT_TRUE(blob_data_handle); scoped_ptr data = blob_data_handle->CreateSnapshot(); @@ -398,14 +396,14 @@ TEST(BlobStorageContextTest, CompoundBlobs) { EXPECT_EQ(*data, blob_data1); // Test a blob composed in part with another blob. - blob_data_handle = context.AddFinishedBlob(&blob_data2); + blob_data_handle = context_.AddFinishedBlob(&blob_data2); data = blob_data_handle->CreateSnapshot(); ASSERT_TRUE(blob_data_handle); ASSERT_TRUE(data); EXPECT_EQ(*data, canonicalized_blob_data2); // Test a blob referring to only data and a disk cache entry. - blob_data_handle = context.AddFinishedBlob(&blob_data3); + blob_data_handle = context_.AddFinishedBlob(&blob_data3); data = blob_data_handle->CreateSnapshot(); ASSERT_TRUE(blob_data_handle); EXPECT_EQ(*data, blob_data3); @@ -414,79 +412,85 @@ TEST(BlobStorageContextTest, CompoundBlobs) { base::RunLoop().RunUntilIdle(); } -TEST(BlobStorageContextTest, PublicBlobUrls) { - BlobStorageContext context; - BlobStorageHost host(&context); +TEST_F(BlobStorageContextTest, PublicBlobUrls) { base::MessageLoop fake_io_message_loop; // Build up a basic blob. const std::string kId("id"); - SetupBasicBlob(&host, kId); + scoped_ptr first_handle = SetupBasicBlob(kId); // Now register a url for that blob. GURL kUrl("blob:id"); - EXPECT_TRUE(host.RegisterPublicBlobURL(kUrl, kId)); + context_.RegisterPublicBlobURL(kUrl, kId); scoped_ptr blob_data_handle = - context.GetBlobDataFromPublicURL(kUrl); + context_.GetBlobDataFromPublicURL(kUrl); ASSERT_TRUE(blob_data_handle.get()); EXPECT_EQ(kId, blob_data_handle->uuid()); scoped_ptr data = blob_data_handle->CreateSnapshot(); blob_data_handle.reset(); + first_handle.reset(); base::RunLoop().RunUntilIdle(); // The url registration should keep the blob alive even after // explicit references are dropped. - EXPECT_TRUE(host.DecrementBlobRefCount(kId)); - blob_data_handle = context.GetBlobDataFromPublicURL(kUrl); + blob_data_handle = context_.GetBlobDataFromPublicURL(kUrl); EXPECT_TRUE(blob_data_handle); blob_data_handle.reset(); base::RunLoop().RunUntilIdle(); // Finally get rid of the url registration and the blob. - EXPECT_TRUE(host.RevokePublicBlobURL(kUrl)); - blob_data_handle = context.GetBlobDataFromPublicURL(kUrl); - EXPECT_TRUE(!blob_data_handle.get()); - EXPECT_FALSE(host.RevokePublicBlobURL(kUrl)); -} - -TEST(BlobStorageContextTest, HostCleanup) { - BlobStorageContext context; - scoped_ptr host(new BlobStorageHost(&context)); - base::MessageLoop fake_io_message_loop; - - // Build up a basic blob and register a url - const std::string kId("id"); - GURL kUrl("blob:id"); - SetupBasicBlob(host.get(), kId); - EXPECT_TRUE(host->RegisterPublicBlobURL(kUrl, kId)); - - // All should disappear upon host deletion. - host.reset(); - scoped_ptr handle = context.GetBlobDataFromPublicURL(kUrl); - EXPECT_TRUE(!handle.get()); - handle = context.GetBlobDataFromUUID(kId); - EXPECT_TRUE(!handle.get()); + context_.RevokePublicBlobURL(kUrl); + blob_data_handle = context_.GetBlobDataFromPublicURL(kUrl); + EXPECT_FALSE(blob_data_handle.get()); + EXPECT_FALSE(context_.registry().HasEntry(kId)); } -TEST(BlobStorageContextTest, EarlyContextDeletion) { - scoped_ptr context(new BlobStorageContext); - BlobStorageHost host(context.get()); +TEST_F(BlobStorageContextTest, TestUnknownBrokenAndBuildingBlobReference) { base::MessageLoop fake_io_message_loop; - - // Deleting the context should not induce crashes. - context.reset(); - - const std::string kId("id"); - GURL kUrl("blob:id"); - EXPECT_FALSE(host.StartBuildingBlob(kId)); - DataElement item; - item.SetToBytes("1", 1); - EXPECT_FALSE(host.AppendBlobDataItem(kId, item)); - EXPECT_FALSE(host.FinishBuildingBlob(kId, "text/plain")); - EXPECT_FALSE(host.RegisterPublicBlobURL(kUrl, kId)); - EXPECT_FALSE(host.IncrementBlobRefCount(kId)); - EXPECT_FALSE(host.DecrementBlobRefCount(kId)); - EXPECT_FALSE(host.RevokePublicBlobURL(kUrl)); + const std::string kBrokenId("broken_id"); + const std::string kBuildingId("building_id"); + const std::string kReferencingId("referencing_id"); + const std::string kUnknownId("unknown_id"); + + // Create a broken blob and a building blob. + context_.CreatePendingBlob(kBuildingId, "", ""); + context_.CreatePendingBlob(kBrokenId, "", ""); + context_.CancelPendingBlob(kBrokenId, IPCBlobCreationCancelCode::UNKNOWN); + EXPECT_TRUE(context_.IsBroken(kBrokenId)); + EXPECT_TRUE(context_.registry().HasEntry(kBrokenId)); + + // Try to create a blob with a reference to an unknown blob. + BlobDataBuilder builder(kReferencingId); + builder.AppendData("data"); + builder.AppendBlob(kUnknownId); + scoped_ptr handle = context_.AddFinishedBlob(builder); + EXPECT_TRUE(handle->IsBroken()); + EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); + handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); + + // Try to create a blob with a reference to the broken blob. + BlobDataBuilder builder2(kReferencingId); + builder2.AppendData("data"); + builder2.AppendBlob(kBrokenId); + handle = context_.AddFinishedBlob(builder2); + EXPECT_TRUE(handle->IsBroken()); + EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); + handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); + + // Try to create a blob with a reference to the building blob. + BlobDataBuilder builder3(kReferencingId); + builder3.AppendData("data"); + builder3.AppendBlob(kBuildingId); + handle = context_.AddFinishedBlob(builder3); + EXPECT_TRUE(handle->IsBroken()); + EXPECT_TRUE(context_.registry().HasEntry(kReferencingId)); + handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_.registry().HasEntry(kReferencingId)); } // TODO(michaeln): tests for the depcrecated url stuff diff --git a/chromium/content/browser/fileapi/blob_storage_host.cc b/chromium/content/browser/fileapi/blob_storage_host.cc deleted file mode 100644 index faf81010dd3..00000000000 --- a/chromium/content/browser/fileapi/blob_storage_host.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/fileapi/blob_storage_host.h" - -#include "base/sequenced_task_runner.h" -#include "base/strings/string_util.h" -#include "storage/browser/blob/blob_storage_context.h" -#include "url/gurl.h" - -using storage::BlobStorageContext; - -namespace content { - -BlobStorageHost::BlobStorageHost(BlobStorageContext* context) - : context_(context->AsWeakPtr()) { -} - -BlobStorageHost::~BlobStorageHost() { - if (!context_.get()) - return; - for (std::set::iterator iter = public_blob_urls_.begin(); - iter != public_blob_urls_.end(); ++iter) { - context_->RevokePublicBlobURL(*iter); - } - for (BlobReferenceMap::iterator iter = blobs_inuse_map_.begin(); - iter != blobs_inuse_map_.end(); ++iter) { - for (int i = 0; i < iter->second; ++i) - context_->DecrementBlobRefCount(iter->first); - } -} - -bool BlobStorageHost::StartBuildingBlob(const std::string& uuid) { - if (!context_.get() || uuid.empty() || context_->IsInUse(uuid)) - return false; - context_->StartBuildingBlob(uuid); - blobs_inuse_map_[uuid] = 1; - return true; -} - -bool BlobStorageHost::AppendBlobDataItem( - const std::string& uuid, - const storage::DataElement& data_item) { - if (!context_.get() || !IsBeingBuiltInHost(uuid)) - return false; - context_->AppendBlobDataItem(uuid, data_item); - return true; -} - -bool BlobStorageHost::CancelBuildingBlob(const std::string& uuid) { - if (!context_.get() || !IsBeingBuiltInHost(uuid)) - return false; - blobs_inuse_map_.erase(uuid); - context_->CancelBuildingBlob(uuid); - return true; -} - -bool BlobStorageHost::FinishBuildingBlob( - const std::string& uuid, const std::string& content_type) { - if (!context_.get() || !IsBeingBuiltInHost(uuid)) - return false; - context_->FinishBuildingBlob(uuid, content_type); - return true; -} - -bool BlobStorageHost::IncrementBlobRefCount(const std::string& uuid) { - if (!context_.get() || !context_->IsInUse(uuid) || - context_->IsBeingBuilt(uuid)) - return false; - context_->IncrementBlobRefCount(uuid); - blobs_inuse_map_[uuid] += 1; - return true; -} - -bool BlobStorageHost::DecrementBlobRefCount(const std::string& uuid) { - if (!context_.get() || !IsInUseInHost(uuid)) - return false; - context_->DecrementBlobRefCount(uuid); - blobs_inuse_map_[uuid] -= 1; - if (blobs_inuse_map_[uuid] == 0) - blobs_inuse_map_.erase(uuid); - return true; -} - -bool BlobStorageHost::RegisterPublicBlobURL( - const GURL& blob_url, const std::string& uuid) { - if (!context_.get() || !IsInUseInHost(uuid) || - context_->IsUrlRegistered(blob_url)) - return false; - context_->RegisterPublicBlobURL(blob_url, uuid); - public_blob_urls_.insert(blob_url); - return true; -} - -bool BlobStorageHost::RevokePublicBlobURL(const GURL& blob_url) { - if (!context_.get() || !IsUrlRegisteredInHost(blob_url)) - return false; - context_->RevokePublicBlobURL(blob_url); - public_blob_urls_.erase(blob_url); - return true; -} - -bool BlobStorageHost::IsInUseInHost(const std::string& uuid) { - return blobs_inuse_map_.find(uuid) != blobs_inuse_map_.end(); -} - -bool BlobStorageHost::IsBeingBuiltInHost(const std::string& uuid) { - return IsInUseInHost(uuid) && context_->IsBeingBuilt(uuid); -} - -bool BlobStorageHost::IsUrlRegisteredInHost(const GURL& blob_url) { - return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); -} - -} // namespace content diff --git a/chromium/content/browser/fileapi/blob_storage_host.h b/chromium/content/browser/fileapi/blob_storage_host.h deleted file mode 100644 index ecfbc569262..00000000000 --- a/chromium/content/browser/fileapi/blob_storage_host.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_FILEAPI_BLOB_STORAGE_HOST_H_ -#define CONTENT_BROWSER_FILEAPI_BLOB_STORAGE_HOST_H_ - -#include -#include -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "content/common/content_export.h" -#include "storage/common/data_element.h" - -class GURL; - -namespace storage { -class BlobDataHandle; -class BlobStorageHost; -class BlobStorageContext; -} - -namespace content { - -// This class handles the logistics of blob storage for a single child process. -// There is one instance per child process. When the child process -// terminates all blob references attibutable to that process go away upon -// destruction of the instance. The class is single threaded and should -// only be used on the IO thread. -class CONTENT_EXPORT BlobStorageHost { - public: - explicit BlobStorageHost(storage::BlobStorageContext* context); - ~BlobStorageHost(); - - // Methods to support the IPC message protocol. - // A false return indicates a problem with the inputs - // like a non-existent or pre-existent uuid or url. - bool StartBuildingBlob(const std::string& uuid) WARN_UNUSED_RESULT; - bool AppendBlobDataItem(const std::string& uuid, - const storage::DataElement& data_item) - WARN_UNUSED_RESULT; - bool CancelBuildingBlob(const std::string& uuid) WARN_UNUSED_RESULT; - bool FinishBuildingBlob(const std::string& uuid, - const std::string& type) WARN_UNUSED_RESULT; - bool IncrementBlobRefCount(const std::string& uuid) WARN_UNUSED_RESULT; - bool DecrementBlobRefCount(const std::string& uuid) WARN_UNUSED_RESULT; - bool RegisterPublicBlobURL(const GURL& blob_url, - const std::string& uuid) WARN_UNUSED_RESULT; - bool RevokePublicBlobURL(const GURL& blob_url) WARN_UNUSED_RESULT; - - private: - typedef std::map BlobReferenceMap; - - bool IsInUseInHost(const std::string& uuid); - bool IsBeingBuiltInHost(const std::string& uuid); - bool IsUrlRegisteredInHost(const GURL& blob_url); - - // Collection of blob ids and a count of how many usages - // of that id are attributable to this consumer. - BlobReferenceMap blobs_inuse_map_; - - // The set of public blob urls coined by this consumer. - std::set public_blob_urls_; - - base::WeakPtr context_; - - DISALLOW_COPY_AND_ASSIGN(BlobStorageHost); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_FILEAPI_BLOB_STORAGE_HOST_H_ diff --git a/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc b/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc index 782a1e581db..bdb430fe14a 100644 --- a/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc +++ b/chromium/content/browser/fileapi/blob_url_request_job_unittest.cc @@ -534,7 +534,7 @@ TEST_F(BlobURLRequestJobTest, TestExtraHeaders) { std::string content_type; EXPECT_TRUE(request_->response_headers()->GetMimeType(&content_type)); EXPECT_EQ(kTestContentType, content_type); - void* iter = NULL; + size_t iter = 0; std::string content_disposition; EXPECT_TRUE(request_->response_headers()->EnumerateHeader( &iter, "Content-Disposition", &content_disposition)); diff --git a/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc b/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc index 055b8f2c97f..6e2fcca758c 100644 --- a/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc +++ b/chromium/content/browser/fileapi/copy_or_move_operation_delegate_unittest.cc @@ -619,8 +619,10 @@ TEST(LocalFileSystemCopyOrMoveOperationTest, ASSERT_TRUE(helper.DirectoryExists(src)); ASSERT_TRUE(helper.DirectoryExists(dest)); + // In the move operation, [file 0, file 2, file 3] are processed as LIFO. + // After file 3 is processed, file 2 is rejected by the validator and the + // operation fails. That is, only file 3 should be in dest. FileSystemTestCaseRecord kMoveDirResultCases[] = { - {false, FILE_PATH_LITERAL("file 0"), 38}, {false, FILE_PATH_LITERAL("file 3"), 0}, }; diff --git a/chromium/content/browser/fileapi/dragged_file_util_unittest.cc b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc index 07c983bad4a..63ba54abae4 100644 --- a/chromium/content/browser/fileapi/dragged_file_util_unittest.cc +++ b/chromium/content/browser/fileapi/dragged_file_util_unittest.cc @@ -15,6 +15,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "build/build_config.h" diff --git a/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc index 15f376ce44b..8a535bbdd06 100644 --- a/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc +++ b/chromium/content/browser/fileapi/file_system_dir_url_request_job_unittest.cc @@ -22,7 +22,6 @@ #include "content/public/test/test_file_system_backend.h" #include "content/public/test/test_file_system_context.h" #include "net/base/net_errors.h" -#include "net/base/net_util.h" #include "net/base/request_priority.h" #include "net/http/http_request_headers.h" #include "net/url_request/url_request.h" @@ -252,9 +251,11 @@ class FileSystemDirURLRequestJobTest : public testing::Test { const std::string& url, bool is_directory, int64_t size) { +#define NUMBER "([0-9-]*)" #define STR "([^\"]*)" icu::UnicodeString pattern("^"); + "\",(0|1)," NUMBER ",\"" STR "\"," NUMBER ",\"" STR "\"\\);"); +#undef NUMBER #undef STR icu::UnicodeString input(entry_line.c_str()); @@ -262,7 +263,7 @@ class FileSystemDirURLRequestJobTest : public testing::Test { icu::RegexMatcher match(pattern, input, 0, status); EXPECT_TRUE(match.find()); - EXPECT_EQ(5, match.groupCount()); + EXPECT_EQ(7, match.groupCount()); EXPECT_EQ(icu::UnicodeString(name.c_str()), match.group(1, status)); EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status)); EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"), @@ -270,10 +271,10 @@ class FileSystemDirURLRequestJobTest : public testing::Test { if (size >= 0) { icu::UnicodeString size_string( base::FormatBytesUnlocalized(size).c_str()); - EXPECT_EQ(size_string, match.group(4, status)); + EXPECT_EQ(size_string, match.group(5, status)); } - icu::UnicodeString date_ustr(match.group(5, status)); + icu::UnicodeString date_ustr(match.group(7, status)); scoped_ptr formatter( icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort)); UErrorCode parse_status = U_ZERO_ERROR; diff --git a/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc b/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc index e3a24c20151..a1e4f8d05bb 100644 --- a/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc +++ b/chromium/content/browser/fileapi/file_system_quota_client_unittest.cc @@ -8,6 +8,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "content/public/test/async_file_test_helper.h" #include "content/public/test/test_file_system_context.h" diff --git a/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc b/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc index 4b2715c29ab..58ab33b6ea4 100644 --- a/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc +++ b/chromium/content/browser/fileapi/file_system_url_request_job_unittest.cc @@ -27,7 +27,6 @@ #include "net/base/load_flags.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" -#include "net/base/net_util.h" #include "net/base/request_priority.h" #include "net/http/http_byte_range.h" #include "net/http/http_request_headers.h" diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.cc b/chromium/content/browser/fileapi/fileapi_message_filter.cc index 8594aef48bd..318d9566448 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter.cc @@ -20,7 +20,6 @@ #include "build/build_config.h" #include "content/browser/bad_message.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/fileapi/blob_storage_host.h" #include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/streams/stream_registry.h" @@ -56,9 +55,7 @@ namespace content { namespace { -const uint32_t kFilteredMessageClasses[] = { - BlobMsgStart, FileSystemMsgStart, -}; +const uint32_t kFilteredMessageClasses[] = {FileSystemMsgStart, BlobMsgStart}; void RevokeFilePermission(int child_id, const base::FilePath& path) { ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( @@ -118,18 +115,13 @@ void FileAPIMessageFilter::OnChannelConnected(int32_t peer_pid) { DCHECK(request_context_); } - blob_storage_host_.reset( - new BlobStorageHost(blob_storage_context_->context())); - operation_runner_ = context_->CreateFileSystemOperationRunner(); } void FileAPIMessageFilter::OnChannelClosing() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // Unregister all the blob and stream URLs that are previously registered in - // this process. - blob_storage_host_.reset(); + // Unregister all stream URLs that are previously registered in this process. for (base::hash_set::const_iterator iter = stream_urls_.begin(); iter != stream_urls_.end(); ++iter) { stream_context_->registry()->UnregisterStream(GURL(*iter)); @@ -171,19 +163,6 @@ bool FileAPIMessageFilter::OnMessageReceived(const IPC::Message& message) { OnDidReceiveSnapshotFile) IPC_MESSAGE_HANDLER(FileSystemHostMsg_SyncGetPlatformPath, OnSyncGetPlatformPath) - IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuilding, OnStartBuildingBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_AppendBlobDataItem, - OnAppendBlobDataItemToBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemory, - OnAppendSharedMemoryToBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuilding, OnFinishBuildingBlob) - IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, - OnIncrementBlobRefCount) - IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, - OnDecrementBlobRefCount) - IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, - OnRegisterPublicBlobURL) - IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL) IPC_MESSAGE_HANDLER(StreamHostMsg_StartBuilding, OnStartBuildingStream) IPC_MESSAGE_HANDLER(StreamHostMsg_AppendBlobDataItem, OnAppendBlobDataItemToStream) @@ -514,90 +493,6 @@ void FileAPIMessageFilter::OnDidReceiveSnapshotFile(int request_id) { in_transit_snapshot_files_.erase(request_id); } -void FileAPIMessageFilter::OnStartBuildingBlob(const std::string& uuid) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->StartBuildingBlob(uuid)); -} - -void FileAPIMessageFilter::OnAppendBlobDataItemToBlob( - const std::string& uuid, - const storage::DataElement& item) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (item.type() == storage::DataElement::TYPE_FILE_FILESYSTEM) { - FileSystemURL filesystem_url(context_->CrackURL(item.filesystem_url())); - if (!FileSystemURLIsValid(context_, filesystem_url) || - !security_policy_->CanReadFileSystemFile(process_id_, filesystem_url)) { - ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); - return; - } - } - if (item.type() == storage::DataElement::TYPE_FILE && - !security_policy_->CanReadFile(process_id_, item.path())) { - ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); - return; - } - if (item.length() == 0) { - bad_message::ReceivedBadMessage(this, - bad_message::FAMF_APPEND_ITEM_TO_BLOB); - return; - } - ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); -} - -void FileAPIMessageFilter::OnAppendSharedMemoryToBlob( - const std::string& uuid, - base::SharedMemoryHandle handle, - size_t buffer_size) { - DCHECK(base::SharedMemory::IsHandleValid(handle)); - if (!buffer_size) { - bad_message::ReceivedBadMessage( - this, bad_message::FAMF_APPEND_SHARED_MEMORY_TO_BLOB); - return; - } -#if defined(OS_WIN) - base::SharedMemory shared_memory(handle, true, PeerHandle()); -#else - base::SharedMemory shared_memory(handle, true); -#endif - if (!shared_memory.Map(buffer_size)) { - ignore_result(blob_storage_host_->CancelBuildingBlob(uuid)); - return; - } - - storage::DataElement item; - item.SetToSharedBytes(static_cast(shared_memory.memory()), - buffer_size); - ignore_result(blob_storage_host_->AppendBlobDataItem(uuid, item)); -} - -void FileAPIMessageFilter::OnFinishBuildingBlob( - const std::string& uuid, const std::string& content_type) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->FinishBuildingBlob(uuid, content_type)); - // TODO(michaeln): check return values once blink has migrated, crbug/174200 -} - -void FileAPIMessageFilter::OnIncrementBlobRefCount(const std::string& uuid) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->IncrementBlobRefCount(uuid)); -} - -void FileAPIMessageFilter::OnDecrementBlobRefCount(const std::string& uuid) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->DecrementBlobRefCount(uuid)); -} - -void FileAPIMessageFilter::OnRegisterPublicBlobURL( - const GURL& public_url, const std::string& uuid) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->RegisterPublicBlobURL(public_url, uuid)); -} - -void FileAPIMessageFilter::OnRevokePublicBlobURL(const GURL& public_url) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ignore_result(blob_storage_host_->RevokePublicBlobURL(public_url)); -} - void FileAPIMessageFilter::OnStartBuildingStream( const GURL& url, const std::string& content_type) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -638,18 +533,15 @@ void FileAPIMessageFilter::OnAppendBlobDataItemToStream( } void FileAPIMessageFilter::OnAppendSharedMemoryToStream( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) { + const GURL& url, base::SharedMemoryHandle handle, + uint32_t buffer_size) { DCHECK(base::SharedMemory::IsHandleValid(handle)); if (!buffer_size) { bad_message::ReceivedBadMessage( this, bad_message::FAMF_APPEND_SHARED_MEMORY_TO_STREAM); return; } -#if defined(OS_WIN) - base::SharedMemory shared_memory(handle, true, PeerHandle()); -#else base::SharedMemory shared_memory(handle, true); -#endif if (!shared_memory.Map(buffer_size)) { OnRemoveStream(url); return; diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h index 74d056fef8e..7f0a20f340f 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.h +++ b/chromium/content/browser/fileapi/fileapi_message_filter.h @@ -46,10 +46,6 @@ class URLRequestContext; class URLRequestContextGetter; } // namespace net -namespace content { -class BlobStorageHost; -} - namespace storage { class ShareableFileReference; class DataElement; @@ -128,21 +124,6 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { const GURL& path); void OnDidReceiveSnapshotFile(int request_id); - // Handlers for BlobHostMsg_ family messages. - - void OnStartBuildingBlob(const std::string& uuid); - void OnAppendBlobDataItemToBlob(const std::string& uuid, - const storage::DataElement& item); - void OnAppendSharedMemoryToBlob(const std::string& uuid, - base::SharedMemoryHandle handle, - size_t buffer_size); - void OnFinishBuildingBlob(const std::string& uuid, - const std::string& content_type); - void OnIncrementBlobRefCount(const std::string& uuid); - void OnDecrementBlobRefCount(const std::string& uuid); - void OnRegisterPublicBlobURL(const GURL& public_url, const std::string& uuid); - void OnRevokePublicBlobURL(const GURL& public_url); - // Handlers for StreamHostMsg_ family messages. // // TODO(tyoshino): Consider renaming BlobData to more generic one as it's now @@ -155,7 +136,7 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { void OnAppendBlobDataItemToStream(const GURL& url, const storage::DataElement& item); void OnAppendSharedMemoryToStream( - const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size); + const GURL& url, base::SharedMemoryHandle handle, uint32_t buffer_size); void OnFlushStream(const GURL& url); void OnFinishBuildingStream(const GURL& url); void OnAbortBuildingStream(const GURL& url); @@ -229,10 +210,6 @@ class CONTENT_EXPORT FileAPIMessageFilter : public BrowserMessageFilter { scoped_ptr operation_runner_; - // Keeps track of blobs used in this process and cleans up - // when the renderer process dies. - scoped_ptr blob_storage_host_; - // Keep track of stream URLs registered in this process. Need to unregister // all of them when the renderer process dies. base::hash_set stream_urls_; diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc index 1fe224e38ec..b7d61e389d7 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc @@ -249,7 +249,7 @@ TEST_F(FileAPIMessageFilterTest, BuildStreamWithSharedMemory) { ASSERT_TRUE(shared_memory->CreateAndMapAnonymous(kFakeData.size())); memcpy(shared_memory->memory(), kFakeData.data(), kFakeData.size()); StreamHostMsg_SyncAppendSharedMemory append_message( - kUrl, shared_memory->handle(), kFakeData.size()); + kUrl, shared_memory->handle(), static_cast(kFakeData.size())); EXPECT_TRUE(filter_->OnMessageReceived(append_message)); StreamHostMsg_FinishBuilding finish_message(kUrl); diff --git a/chromium/content/browser/fileapi/local_file_util_unittest.cc b/chromium/content/browser/fileapi/local_file_util_unittest.cc index 28ff6b3ba9d..071a122279a 100644 --- a/chromium/content/browser/fileapi/local_file_util_unittest.cc +++ b/chromium/content/browser/fileapi/local_file_util_unittest.cc @@ -11,6 +11,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/content/browser/fileapi/native_file_util_unittest.cc b/chromium/content/browser/fileapi/native_file_util_unittest.cc index 210f8b9cca5..5a91454bbfc 100644 --- a/chromium/content/browser/fileapi/native_file_util_unittest.cc +++ b/chromium/content/browser/fileapi/native_file_util_unittest.cc @@ -11,6 +11,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "storage/browser/fileapi/native_file_util.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc index abcdf54e65b..8c00f0c7c31 100644 --- a/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc +++ b/chromium/content/browser/fileapi/recursive_operation_delegate_unittest.cc @@ -11,6 +11,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" diff --git a/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc b/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc index 8338304e6a6..ce89ae6b8e7 100644 --- a/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc +++ b/chromium/content/browser/fileapi/sandbox_origin_database_unittest.cc @@ -15,6 +15,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "content/browser/fileapi/sandbox_database_test_helper.h" #include "storage/browser/fileapi/sandbox_origin_database.h" diff --git a/chromium/content/browser/fileapi/upload_file_system_file_element_reader.h b/chromium/content/browser/fileapi/upload_file_system_file_element_reader.h index 5510ce540b6..cba9fa795d8 100644 --- a/chromium/content/browser/fileapi/upload_file_system_file_element_reader.h +++ b/chromium/content/browser/fileapi/upload_file_system_file_element_reader.h @@ -8,6 +8,7 @@ #include #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "content/common/content_export.h" diff --git a/chromium/content/browser/frame_host/OWNERS b/chromium/content/browser/frame_host/OWNERS index 2d5011da465..cb3028c329e 100644 --- a/chromium/content/browser/frame_host/OWNERS +++ b/chromium/content/browser/frame_host/OWNERS @@ -1 +1,2 @@ +alexmos@chromium.org clamy@chromium.org diff --git a/chromium/content/browser/frame_host/PRESUBMIT.py b/chromium/content/browser/frame_host/PRESUBMIT.py new file mode 100644 index 00000000000..d3afe71d89e --- /dev/null +++ b/chromium/content/browser/frame_host/PRESUBMIT.py @@ -0,0 +1,53 @@ +# 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. +"""Presubmit script for //content/browser/frame_host. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details about the presubmit API built into depot_tools. +""" + +import re + + +def _GetTryMasters(project, change): + return { + 'tryserver.chromium.linux': { + 'linux_site_isolation': [], + }, + } + + +def GetPreferredTryMasters(project, change): + # TODO(nick, dcheng): Using the value of _GetTryMasters() instead of an empty + # value here would cause 'git cl try' to include the site isolation trybots, + # which would be nice. But it has the side effect of replacing, rather than + # augmenting, the default set of try servers. Re-enable this when we figure + # out a way to augment the default set. + return {} + + +def PostUploadHook(cl, change, output_api): + """git cl upload will call this hook after the issue is created/modified. + + This hook adds extra try bots to the CL description in order to run site + isolation tests in addition to CQ try bots. + """ + rietveld_obj = cl.RpcServer() + issue = cl.issue + description = rietveld_obj.get_description(issue) + if re.search(r'^CQ_INCLUDE_TRYBOTS=.*', description, re.M | re.I): + return [] + + masters = _GetTryMasters(None, change) + results = [] + new_description = description + new_description += '\nCQ_INCLUDE_TRYBOTS=%s' % ';'.join( + '%s:%s' % (master, ','.join(bots)) + for master, bots in masters.iteritems()) + results.append(output_api.PresubmitNotifyResult( + 'Automatically added site isolation trybots to run tests on CQ.')) + + rietveld_obj.update_description(issue, new_description) + + return results diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.cc b/chromium/content/browser/frame_host/cross_process_frame_connector.cc index 1d8b23c0411..b5e415ced16 100644 --- a/chromium/content/browser/frame_host/cross_process_frame_connector.cc +++ b/chromium/content/browser/frame_host/cross_process_frame_connector.cc @@ -17,7 +17,7 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/common/frame_messages.h" -#include "content/common/gpu/gpu_messages.h" +#include "gpu/ipc/common/gpu_messages.h" #include "third_party/WebKit/public/web/WebInputEvent.h" namespace content { @@ -38,10 +38,6 @@ bool CrossProcessFrameConnector::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg) - IPC_MESSAGE_HANDLER(FrameHostMsg_CompositorFrameSwappedACK, - OnCompositorFrameSwappedACK) - IPC_MESSAGE_HANDLER(FrameHostMsg_ReclaimCompositorResources, - OnReclaimCompositorResources) IPC_MESSAGE_HANDLER(FrameHostMsg_ForwardInputEvent, OnForwardInputEvent) IPC_MESSAGE_HANDLER(FrameHostMsg_FrameRectChanged, OnFrameRectChanged) IPC_MESSAGE_HANDLER(FrameHostMsg_VisibilityChanged, OnVisibilityChanged) @@ -67,7 +63,7 @@ void CrossProcessFrameConnector::set_view( if (view_) { view_->set_cross_process_frame_connector(this); SetDeviceScaleFactor(device_scale_factor_); - SetSize(child_frame_rect_); + SetRect(child_frame_rect_); } } @@ -76,20 +72,6 @@ void CrossProcessFrameConnector::RenderProcessGone() { frame_proxy_in_parent_renderer_->GetRoutingID())); } -void CrossProcessFrameConnector::ChildFrameCompositorFrameSwapped( - uint32_t output_surface_id, - int host_id, - int route_id, - scoped_ptr frame) { - FrameMsg_CompositorFrameSwapped_Params params; - frame->AssignTo(¶ms.frame); - params.output_surface_id = output_surface_id; - params.producing_route_id = route_id; - params.producing_host_id = host_id; - frame_proxy_in_parent_renderer_->Send(new FrameMsg_CompositorFrameSwapped( - frame_proxy_in_parent_renderer_->GetRoutingID(), params)); -} - void CrossProcessFrameConnector::SetChildFrameSurface( const cc::SurfaceId& surface_id, const gfx::Size& frame_size, @@ -120,29 +102,13 @@ void CrossProcessFrameConnector::OnRequireSequence( surface->AddDestructionDependency(sequence); } -void CrossProcessFrameConnector::OnCompositorFrameSwappedACK( - const FrameHostMsg_CompositorFrameSwappedACK_Params& params) { - RenderWidgetHostImpl::SendSwapCompositorFrameAck(params.producing_route_id, - params.output_surface_id, - params.producing_host_id, - params.ack); -} - -void CrossProcessFrameConnector::OnReclaimCompositorResources( - const FrameHostMsg_ReclaimCompositorResources_Params& params) { - RenderWidgetHostImpl::SendReclaimCompositorResources(params.route_id, - params.output_surface_id, - params.renderer_host_id, - params.ack); -} - void CrossProcessFrameConnector::OnInitializeChildFrame(gfx::Rect frame_rect, float scale_factor) { if (scale_factor != device_scale_factor_) SetDeviceScaleFactor(scale_factor); if (!frame_rect.size().IsEmpty()) - SetSize(frame_rect); + SetRect(frame_rect); } gfx::Rect CrossProcessFrameConnector::ChildFrameRect() { @@ -150,20 +116,10 @@ gfx::Rect CrossProcessFrameConnector::ChildFrameRect() { } void CrossProcessFrameConnector::GetScreenInfo(blink::WebScreenInfo* results) { - // Inner WebContents's root FrameTreeNode does not have a parent(), so - // GetRenderWidgetHostView() call below will fail. - // TODO(lazyboy): Fix this. - if (frame_proxy_in_parent_renderer_->frame_tree_node() - ->render_manager() - ->ForInnerDelegate()) { - DCHECK(frame_proxy_in_parent_renderer_->frame_tree_node()->IsMainFrame()); - return; + auto parent_view = GetParentRenderWidgetHostView(); + if (parent_view) { + parent_view->GetScreenInfo(results); } - - RenderWidgetHostView* rwhv = - frame_proxy_in_parent_renderer_->GetRenderWidgetHostView(); - if (rwhv) - static_cast(rwhv)->GetScreenInfo(results); } void CrossProcessFrameConnector::UpdateCursor(const WebCursor& cursor) { @@ -172,15 +128,23 @@ void CrossProcessFrameConnector::UpdateCursor(const WebCursor& cursor) { root_view->UpdateCursor(cursor); } -void CrossProcessFrameConnector::TransformPointToRootCoordSpace( +gfx::Point CrossProcessFrameConnector::TransformPointToRootCoordSpace( const gfx::Point& point, - cc::SurfaceId surface_id, - gfx::Point* transformed_point) { + cc::SurfaceId surface_id) { + gfx::Point transformed_point = point; RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView(); - *transformed_point = point; if (root_view) root_view->TransformPointToLocalCoordSpace(point, surface_id, - transformed_point); + &transformed_point); + return transformed_point; +} + +void CrossProcessFrameConnector::ForwardProcessAckedTouchEvent( + const TouchEventWithLatencyInfo& touch, + InputEventAckState ack_result) { + auto main_view = GetRootRenderWidgetHostView(); + if (main_view) + main_view->ProcessAckedTouchEvent(touch, ack_result); } bool CrossProcessFrameConnector::HasFocus() { @@ -190,6 +154,12 @@ bool CrossProcessFrameConnector::HasFocus() { return false; } +void CrossProcessFrameConnector::FocusRootView() { + RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView(); + if (root_view) + root_view->Focus(); +} + void CrossProcessFrameConnector::OnForwardInputEvent( const blink::WebInputEvent* event) { if (!view_) @@ -202,6 +172,10 @@ void CrossProcessFrameConnector::OnForwardInputEvent( ? manager->GetOuterRenderWidgetHostForKeyboardInput() : frame_proxy_in_parent_renderer_->GetRenderViewHost()->GetWidget(); + // TODO(wjmaclean): We should remove these forwarding functions, since they + // are directly target using RenderWidgetHostInputEventRouter. But neither + // pathway is currently handling gesture events, so that needs to be fixed + // in a subsequent CL. if (blink::WebInputEvent::isKeyboardEventType(event->type)) { if (!parent_widget->GetLastKeyboardEvent()) return; @@ -226,13 +200,26 @@ void CrossProcessFrameConnector::OnForwardInputEvent( void CrossProcessFrameConnector::OnFrameRectChanged( const gfx::Rect& frame_rect) { if (!frame_rect.size().IsEmpty()) - SetSize(frame_rect); + SetRect(frame_rect); } void CrossProcessFrameConnector::OnVisibilityChanged(bool visible) { if (!view_) return; + // If there is an inner WebContents, it should be notified of the change in + // the visibility. The Show/Hide methods will not be called if an inner + // WebContents exists since the corresponding WebContents will itself call + // Show/Hide on all the RenderWidgetHostViews (including this) one. + if (frame_proxy_in_parent_renderer_->frame_tree_node() + ->render_manager() + ->ForInnerDelegate()) { + RenderWidgetHostImpl::From(view_->GetRenderWidgetHost()) + ->delegate() + ->OnRenderFrameProxyVisibilityChanged(visible); + return; + } + if (visible) view_->Show(); else @@ -249,10 +236,27 @@ void CrossProcessFrameConnector::SetDeviceScaleFactor(float scale_factor) { } } -void CrossProcessFrameConnector::SetSize(gfx::Rect frame_rect) { +void CrossProcessFrameConnector::SetRect(const gfx::Rect& frame_rect) { + gfx::Rect old_rect = child_frame_rect_; child_frame_rect_ = frame_rect; - if (view_) - view_->SetSize(frame_rect.size()); + if (view_) { + view_->SetBounds(frame_rect); + + // Out-of-process iframes nested underneath this one implicitly have their + // view rects changed when their ancestor is repositioned, and therefore + // need to have their screen rects updated. + FrameTreeNode* proxy_node = + frame_proxy_in_parent_renderer_->frame_tree_node(); + if (old_rect.x() != child_frame_rect_.x() || + old_rect.y() != child_frame_rect_.y()) { + for (FrameTreeNode* node : + proxy_node->frame_tree()->SubtreeNodes(proxy_node)) { + if (node != proxy_node && + node->current_frame_host()->GetRenderWidgetHost()) + node->current_frame_host()->GetRenderWidgetHost()->SendScreenRects(); + } + } + } } RenderWidgetHostViewBase* @@ -270,4 +274,27 @@ CrossProcessFrameConnector::GetRootRenderWidgetHostView() { return static_cast(top_host->GetView()); } +RenderWidgetHostViewBase* +CrossProcessFrameConnector::GetParentRenderWidgetHostView() { + FrameTreeNode* parent = + frame_proxy_in_parent_renderer_->frame_tree_node()->parent(); + + if (!parent && + frame_proxy_in_parent_renderer_->frame_tree_node() + ->render_manager() + ->GetOuterDelegateNode()) { + parent = frame_proxy_in_parent_renderer_->frame_tree_node() + ->render_manager() + ->GetOuterDelegateNode() + ->parent(); + } + + if (parent) { + return static_cast( + parent->current_frame_host()->GetView()); + } + + return nullptr; +} + } // namespace content diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.h b/chromium/content/browser/frame_host/cross_process_frame_connector.h index 6f31ec606fa..0eebdbb5158 100644 --- a/chromium/content/browser/frame_host/cross_process_frame_connector.h +++ b/chromium/content/browser/frame_host/cross_process_frame_connector.h @@ -8,7 +8,9 @@ #include #include "cc/output/compositor_frame.h" +#include "content/browser/renderer_host/event_with_latency_info.h" #include "content/common/content_export.h" +#include "content/common/input/input_event_ack_state.h" #include "ui/gfx/geometry/rect.h" namespace blink { @@ -25,9 +27,6 @@ namespace IPC { class Message; } -struct FrameHostMsg_CompositorFrameSwappedACK_Params; -struct FrameHostMsg_ReclaimCompositorResources_Params; - namespace content { class RenderFrameProxyHost; class RenderWidgetHostImpl; @@ -87,11 +86,6 @@ class CONTENT_EXPORT CrossProcessFrameConnector { void RenderProcessGone(); - virtual void ChildFrameCompositorFrameSwapped( - uint32_t output_surface_id, - int host_id, - int route_id, - scoped_ptr frame); virtual void SetChildFrameSurface(const cc::SurfaceId& surface_id, const gfx::Size& frame_size, float scale_factor, @@ -101,20 +95,31 @@ class CONTENT_EXPORT CrossProcessFrameConnector { float device_scale_factor() const { return device_scale_factor_; } void GetScreenInfo(blink::WebScreenInfo* results); void UpdateCursor(const WebCursor& cursor); - void TransformPointToRootCoordSpace(const gfx::Point& point, - cc::SurfaceId surface_id, - gfx::Point* transformed_point); + gfx::Point TransformPointToRootCoordSpace(const gfx::Point& point, + cc::SurfaceId surface_id); + // Pass acked touch events to the root view for gesture processing. + void ForwardProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch, + InputEventAckState ack_result); // Determines whether the root RenderWidgetHostView (and thus the current // page) has focus. bool HasFocus(); + // Focuses the root RenderWidgetHostView. + void FocusRootView(); + + // Returns the parent RenderWidgetHostView or nullptr it it doesn't have one. + RenderWidgetHostViewBase* GetParentRenderWidgetHostView(); + + // Returns the view for the top-level frame under the same WebContents. + RenderWidgetHostViewBase* GetRootRenderWidgetHostView(); + + // Exposed for tests. + RenderWidgetHostViewBase* GetRootRenderWidgetHostViewForTesting() { + return GetRootRenderWidgetHostView(); + } private: // Handlers for messages received from the parent frame. - void OnCompositorFrameSwappedACK( - const FrameHostMsg_CompositorFrameSwappedACK_Params& params); - void OnReclaimCompositorResources( - const FrameHostMsg_ReclaimCompositorResources_Params& params); void OnForwardInputEvent(const blink::WebInputEvent* event); void OnFrameRectChanged(const gfx::Rect& frame_rect); void OnVisibilityChanged(bool visible); @@ -124,10 +129,7 @@ class CONTENT_EXPORT CrossProcessFrameConnector { const cc::SurfaceSequence& sequence); void SetDeviceScaleFactor(float scale_factor); - void SetSize(gfx::Rect frame_rect); - - // Retrieve the view for the top-level frame under the same WebContents. - RenderWidgetHostViewBase* GetRootRenderWidgetHostView(); + void SetRect(const gfx::Rect& frame_rect); // The RenderFrameProxyHost that routes messages to the parent frame's // renderer process. diff --git a/chromium/content/browser/frame_host/debug_urls.cc b/chromium/content/browser/frame_host/debug_urls.cc index f3190a74acb..833b0063ab4 100644 --- a/chromium/content/browser/frame_host/debug_urls.cc +++ b/chromium/content/browser/frame_host/debug_urls.cc @@ -31,6 +31,11 @@ namespace content { +class ScopedAllowWaitForDebugURL { + private: + base::ThreadRestrictions::ScopedAllowWait wait; +}; + namespace { // Define the Asan debug URLs. @@ -164,14 +169,13 @@ bool HandleAsanDebugURL(const GURL& url) { return true; } +void HangCurrentThread() { + ScopedAllowWaitForDebugURL allow_wait; + base::WaitableEvent(false, false).Wait(); +} } // namespace -class ScopedAllowWaitForDebugURL { - private: - base::ThreadRestrictions::ScopedAllowWait wait; -}; - bool HandleDebugURL(const GURL& url, ui::PageTransition transition) { // Ensure that the user explicitly navigated to this URL, unless // kEnableGpuBenchmarking is enabled by Telemetry. @@ -199,8 +203,16 @@ bool HandleDebugURL(const GURL& url, ui::PageTransition transition) { } if (url == GURL(kChromeUIBrowserUIHang)) { - ScopedAllowWaitForDebugURL allow_wait; - base::WaitableEvent(false, false).Wait(); + HangCurrentThread(); + return true; + } + + if (url == GURL(kChromeUIDelayedBrowserUIHang)) { + // Webdriver-safe url to hang the ui thread. Webdriver waits for the onload + // event in javascript which needs a little more time to fire. + BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, + base::Bind(&HangCurrentThread), + base::TimeDelta::FromSeconds(2)); return true; } diff --git a/chromium/content/browser/frame_host/frame_mojo_shell.cc b/chromium/content/browser/frame_host/frame_mojo_shell.cc index ece035648f4..556bfc9152f 100644 --- a/chromium/content/browser/frame_host/frame_mojo_shell.cc +++ b/chromium/content/browser/frame_host/frame_mojo_shell.cc @@ -9,12 +9,15 @@ #include "build/build_config.h" #include "content/browser/mojo/mojo_shell_context.h" #include "content/common/mojo/service_registry_impl.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/site_instance.h" #include "content/public/common/content_client.h" +#include "mojo/common/url_type_converters.h" -#if defined(OS_ANDROID) && defined(ENABLE_MOJO_MEDIA) +#if defined(OS_ANDROID) && defined(ENABLE_MOJO_CDM) #include "content/browser/media/android/provision_fetcher_impl.h" #endif @@ -24,7 +27,7 @@ namespace { void RegisterFrameMojoShellServices(ServiceRegistry* registry, RenderFrameHost* render_frame_host) { -#if defined(OS_ANDROID) && defined(ENABLE_MOJO_MEDIA) +#if defined(OS_ANDROID) && defined(ENABLE_MOJO_CDM) registry->AddService( base::Bind(&ProvisionFetcherImpl::Create, render_frame_host)); #endif @@ -39,35 +42,32 @@ FrameMojoShell::FrameMojoShell(RenderFrameHost* frame_host) FrameMojoShell::~FrameMojoShell() { } -void FrameMojoShell::BindRequest( - mojo::InterfaceRequest shell_request) { - bindings_.AddBinding(this, std::move(shell_request)); +void FrameMojoShell::BindRequest(mojo::shell::mojom::ConnectorRequest request) { + connectors_.AddBinding(this, std::move(request)); } // TODO(xhwang): Currently no callers are exposing |exposed_services|. So we // drop it and replace it with services we provide in the browser. In the // future we may need to support both. -void FrameMojoShell::ConnectToApplication( - mojo::URLRequestPtr application_url, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr /* exposed_services */, - mojo::CapabilityFilterPtr filter, - const ConnectToApplicationCallback& callback) { - mojo::ServiceProviderPtr frame_services; +void FrameMojoShell::Connect( + mojo::shell::mojom::IdentityPtr target, + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr /* exposed_services */, + mojo::shell::mojom::ClientProcessConnectionPtr client_process_connection, + const mojo::shell::mojom::Connector::ConnectCallback& callback) { + mojo::shell::mojom::InterfaceProviderPtr frame_services; service_provider_bindings_.AddBinding(GetServiceRegistry(), GetProxy(&frame_services)); - - mojo::shell::CapabilityFilter capability_filter = - mojo::shell::GetPermissiveCapabilityFilter(); - if (!filter.is_null()) - capability_filter = filter->filter.To(); + std::string mojo_user_id = BrowserContext::GetMojoUserIdFor( + frame_host_->GetProcess()->GetBrowserContext()); MojoShellContext::ConnectToApplication( - GURL(application_url->url), frame_host_->GetSiteInstance()->GetSiteURL(), - std::move(services), std::move(frame_services), capability_filter, - callback); + mojo_user_id, target->name, + frame_host_->GetSiteInstance()->GetSiteURL().spec(), std::move(services), + std::move(frame_services), callback); } -void FrameMojoShell::QuitApplication() { +void FrameMojoShell::Clone(mojo::shell::mojom::ConnectorRequest request) { + connectors_.AddBinding(this, std::move(request)); } ServiceRegistryImpl* FrameMojoShell::GetServiceRegistry() { diff --git a/chromium/content/browser/frame_host/frame_mojo_shell.h b/chromium/content/browser/frame_host/frame_mojo_shell.h index 08a60e9b831..893ade427f4 100644 --- a/chromium/content/browser/frame_host/frame_mojo_shell.h +++ b/chromium/content/browser/frame_host/frame_mojo_shell.h @@ -7,42 +7,44 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "mojo/common/weak_binding_set.h" +#include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" +#include "mojo/shell/public/interfaces/connector.mojom.h" namespace content { class RenderFrameHost; class ServiceRegistryImpl; -// This provides the |mojo::Shell| service interface to each frame's -// ServiceRegistry, giving frames the ability to connect to Mojo applications. -class FrameMojoShell : public mojo::Shell { +// This provides the |mojo::shell::mojom::Shell| service interface to each +// frame's ServiceRegistry, giving frames the ability to connect to Mojo +// applications. +class FrameMojoShell : public mojo::shell::mojom::Connector { public: explicit FrameMojoShell(RenderFrameHost* frame_host); ~FrameMojoShell() override; - void BindRequest(mojo::InterfaceRequest shell_request); + void BindRequest(mojo::shell::mojom::ConnectorRequest request); private: - // mojo::Shell: - void ConnectToApplication( - mojo::URLRequestPtr application_url, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services, - mojo::CapabilityFilterPtr filter, - const ConnectToApplicationCallback& callback) override; - void QuitApplication() override; + // mojo::Connector: + void Connect( + mojo::shell::mojom::IdentityPtr target, + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services, + mojo::shell::mojom::ClientProcessConnectionPtr client_process_connection, + const mojo::shell::mojom::Connector::ConnectCallback& callback) override; + void Clone(mojo::shell::mojom::ConnectorRequest request) override; ServiceRegistryImpl* GetServiceRegistry(); RenderFrameHost* frame_host_; - mojo::WeakBindingSet bindings_; + mojo::BindingSet connectors_; // ServiceRegistry providing browser services to connected applications. scoped_ptr service_registry_; - mojo::WeakBindingSet service_provider_bindings_; + mojo::BindingSet + service_provider_bindings_; DISALLOW_COPY_AND_ASSIGN(FrameMojoShell); }; diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.cc b/chromium/content/browser/frame_host/frame_navigation_entry.cc index a7a414e85a3..fe8809c8fc6 100644 --- a/chromium/content/browser/frame_host/frame_navigation_entry.cc +++ b/chromium/content/browser/frame_host/frame_navigation_entry.cc @@ -4,6 +4,8 @@ #include "content/browser/frame_host/frame_navigation_entry.h" +#include + namespace content { FrameNavigationEntry::FrameNavigationEntry(int frame_tree_node_id) @@ -12,18 +14,19 @@ FrameNavigationEntry::FrameNavigationEntry(int frame_tree_node_id) document_sequence_number_(-1) { } -FrameNavigationEntry::FrameNavigationEntry(int frame_tree_node_id, - const std::string& frame_unique_name, - int64_t item_sequence_number, - int64_t document_sequence_number, - SiteInstanceImpl* site_instance, - const GURL& url, - const Referrer& referrer) +FrameNavigationEntry::FrameNavigationEntry( + int frame_tree_node_id, + const std::string& frame_unique_name, + int64_t item_sequence_number, + int64_t document_sequence_number, + scoped_refptr site_instance, + const GURL& url, + const Referrer& referrer) : frame_tree_node_id_(frame_tree_node_id), frame_unique_name_(frame_unique_name), item_sequence_number_(item_sequence_number), document_sequence_number_(document_sequence_number), - site_instance_(site_instance), + site_instance_(std::move(site_instance)), url_(url), referrer_(referrer) {} diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.h b/chromium/content/browser/frame_host/frame_navigation_entry.h index 67a27063700..8527c4aed41 100644 --- a/chromium/content/browser/frame_host/frame_navigation_entry.h +++ b/chromium/content/browser/frame_host/frame_navigation_entry.h @@ -33,7 +33,7 @@ class CONTENT_EXPORT FrameNavigationEntry const std::string& frame_unique_name, int64_t item_sequence_number, int64_t document_sequence_number, - SiteInstanceImpl* site_instance, + scoped_refptr site_instance, const GURL& url, const Referrer& referrer); @@ -88,8 +88,8 @@ class CONTENT_EXPORT FrameNavigationEntry // a SiteInstance must live in the same process. This is a refcounted pointer // that keeps the SiteInstance (not necessarily the process) alive as long as // this object remains in the session history. - void set_site_instance(SiteInstanceImpl* site_instance) { - site_instance_ = site_instance; + void set_site_instance(scoped_refptr site_instance) { + site_instance_ = std::move(site_instance); } SiteInstanceImpl* site_instance() const { return site_instance_.get(); } diff --git a/chromium/content/browser/frame_host/frame_tree.cc b/chromium/content/browser/frame_host/frame_tree.cc index e92d28f80be..388c878ba3b 100644 --- a/chromium/content/browser/frame_host/frame_tree.cc +++ b/chromium/content/browser/frame_host/frame_tree.cc @@ -28,84 +28,60 @@ namespace content { namespace { -// Used with FrameTree::ForEach() to search for the FrameTreeNode -// corresponding to |frame_tree_node_id| within a specific FrameTree. -bool FrameTreeNodeForId(int frame_tree_node_id, - FrameTreeNode** out_node, - FrameTreeNode* node) { - if (node->frame_tree_node_id() == frame_tree_node_id) { - *out_node = node; - // Terminate iteration once the node has been found. - return false; - } - return true; +// Helper function to collect SiteInstances involved in rendering a single +// FrameTree (which is a subset of SiteInstances in main frame's proxy_hosts_ +// because of openers). +std::set CollectSiteInstances(FrameTree* tree) { + std::set instances; + for (FrameTreeNode* node : tree->Nodes()) + instances.insert(node->current_frame_host()->GetSiteInstance()); + return instances; } -// Used with FrameTree::ForEach() to search for the FrameTreeNode with the given -// |name| within a specific FrameTree. -bool FrameTreeNodeForName(const std::string& name, - FrameTreeNode** out_node, - FrameTreeNode* node) { - if (node->frame_name() == name) { - *out_node = node; - // Terminate iteration once the node has been found. - return false; +} // namespace + +FrameTree::NodeIterator::NodeIterator(const NodeIterator& other) = default; + +FrameTree::NodeIterator::~NodeIterator() {} + +FrameTree::NodeIterator& FrameTree::NodeIterator::operator++() { + for (size_t i = 0; i < current_node_->child_count(); ++i) { + FrameTreeNode* child = current_node_->child_at(i); + if (child == node_to_skip_) + continue; + queue_.push(child); } - return true; -} -bool CreateProxyForSiteInstance(const scoped_refptr& instance, - FrameTreeNode* node) { - // If a new frame is created in the current SiteInstance, other frames in - // that SiteInstance don't need a proxy for the new frame. - SiteInstance* current_instance = - node->render_manager()->current_frame_host()->GetSiteInstance(); - if (current_instance != instance.get()) - node->render_manager()->CreateRenderFrameProxy(instance.get()); - return true; -} + if (!queue_.empty()) { + current_node_ = queue_.front(); + queue_.pop(); + } else { + current_node_ = nullptr; + } -// Helper function used with FrameTree::ForEach() for retrieving the total -// loading progress and number of frames in a frame tree. -bool CollectLoadProgress(double* progress, - int* frame_count, - FrameTreeNode* node) { - // Ignore the current frame if it has not started loading. - if (!node->has_started_loading()) - return true; - - // Collect progress. - *progress += node->loading_progress(); - (*frame_count)++; - return true; + return *this; } -// Helper function used with FrameTree::ForEach() to reset the load progress. -bool ResetNodeLoadProgress(FrameTreeNode* node) { - node->reset_loading_progress(); - return true; +bool FrameTree::NodeIterator::operator==(const NodeIterator& rhs) const { + return current_node_ == rhs.current_node_; } -// Helper function used with FrameTree::ForEach() to check if at least one of -// the nodes is loading. -bool IsNodeLoading(bool* is_loading, FrameTreeNode* node) { - if (node->IsLoading()) { - // There is at least one node loading, so abort traversal. - *is_loading = true; - return false; - } - return true; +FrameTree::NodeIterator::NodeIterator(FrameTreeNode* starting_node, + FrameTreeNode* node_to_skip) + : current_node_(starting_node != node_to_skip ? starting_node : nullptr), + node_to_skip_(node_to_skip) {} + +FrameTree::NodeIterator FrameTree::NodeRange::begin() { + return NodeIterator(root_, node_to_skip_); } -// Helper function used with FrameTree::ForEach to collect SiteInstances -// involved in rendering a single FrameTree (which is a subset of SiteInstances -// in main frame's proxy_hosts_ because of openers). -bool CollectSiteInstances(std::set* set, FrameTreeNode* node) { - set->insert(node->current_frame_host()->GetSiteInstance()); - return true; +FrameTree::NodeIterator FrameTree::NodeRange::end() { + return NodeIterator(nullptr, nullptr); } -} // namespace +FrameTree::NodeRange::NodeRange(FrameTreeNode* root, + FrameTreeNode* node_to_skip) + : root_(root), node_to_skip_(node_to_skip) {} FrameTree::FrameTree(Navigator* navigator, RenderFrameHostDelegate* render_frame_delegate, @@ -126,7 +102,7 @@ FrameTree::FrameTree(Navigator* navigator, // document scope. blink::WebTreeScopeType::Document, std::string(), - blink::WebSandboxFlags::None, + std::string(), blink::WebFrameOwnerProperties())), focused_frame_tree_node_id_(-1), load_progress_(0.0) {} @@ -137,9 +113,11 @@ FrameTree::~FrameTree() { } FrameTreeNode* FrameTree::FindByID(int frame_tree_node_id) { - FrameTreeNode* node = nullptr; - ForEach(base::Bind(&FrameTreeNodeForId, frame_tree_node_id, &node)); - return node; + for (FrameTreeNode* node : Nodes()) { + if (node->frame_tree_node_id() == frame_tree_node_id) + return node; + } + return nullptr; } FrameTreeNode* FrameTree::FindByRoutingID(int process_id, int routing_id) { @@ -166,34 +144,24 @@ FrameTreeNode* FrameTree::FindByName(const std::string& name) { if (name.empty()) return root_; - FrameTreeNode* node = nullptr; - ForEach(base::Bind(&FrameTreeNodeForName, name, &node)); - return node; -} + for (FrameTreeNode* node : Nodes()) { + if (node->frame_name() == name) + return node; + } -void FrameTree::ForEach( - const base::Callback& on_node) const { - ForEach(on_node, nullptr); + return nullptr; } -void FrameTree::ForEach( - const base::Callback& on_node, - FrameTreeNode* skip_this_subtree) const { - std::queue queue; - queue.push(root_); - - while (!queue.empty()) { - FrameTreeNode* node = queue.front(); - queue.pop(); - if (skip_this_subtree == node) - continue; +FrameTree::NodeRange FrameTree::Nodes() { + return NodesExcept(nullptr); +} - if (!on_node.Run(node)) - break; +FrameTree::NodeRange FrameTree::SubtreeNodes(FrameTreeNode* subtree_root) { + return NodeRange(subtree_root, nullptr); +} - for (size_t i = 0; i < node->child_count(); ++i) - queue.push(node->child_at(i)); - } +FrameTree::NodeRange FrameTree::NodesExcept(FrameTreeNode* node_to_skip) { + return NodeRange(root_, node_to_skip); } bool FrameTree::AddFrame( @@ -202,6 +170,7 @@ bool FrameTree::AddFrame( int new_routing_id, blink::WebTreeScopeType scope, const std::string& frame_name, + const std::string& frame_unique_name, blink::WebSandboxFlags sandbox_flags, const blink::WebFrameOwnerProperties& frame_owner_properties) { CHECK_NE(new_routing_id, MSG_ROUTING_NONE); @@ -218,9 +187,14 @@ bool FrameTree::AddFrame( make_scoped_ptr(new FrameTreeNode( this, parent->navigator(), render_frame_delegate_, render_view_delegate_, render_widget_delegate_, manager_delegate_, - scope, frame_name, sandbox_flags, frame_owner_properties)), + scope, frame_name, frame_unique_name, frame_owner_properties)), process_id, new_routing_id); + // Set sandbox flags and make them effective immediately, since initial + // sandbox flags should apply to the initial empty document in the frame. + added_node->SetPendingSandboxFlags(sandbox_flags); + added_node->CommitPendingSandboxFlags(); + // Now that the new node is part of the FrameTree and has a RenderFrameHost, // we can announce the creation of the initial RenderFrame which already // exists in the renderer process. @@ -241,30 +215,28 @@ void FrameTree::RemoveFrame(FrameTreeNode* child) { void FrameTree::CreateProxiesForSiteInstance( FrameTreeNode* source, SiteInstance* site_instance) { - // Create the swapped out RVH for the new SiteInstance. This will create - // a top-level swapped out RFH as well, which will then be wrapped by a - // RenderFrameProxyHost. + // Create the RenderFrameProxyHost for the new SiteInstance. if (!source || !source->IsMainFrame()) { RenderViewHostImpl* render_view_host = GetRenderViewHost(site_instance); if (!render_view_host) { - if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { - root()->render_manager()->CreateRenderFrameProxy(site_instance); - } else { - root()->render_manager()->CreateRenderFrame( - site_instance, CREATE_RF_SWAPPED_OUT | CREATE_RF_HIDDEN, nullptr); - } + root()->render_manager()->CreateRenderFrameProxy(site_instance); } else { root()->render_manager()->EnsureRenderViewInitialized(render_view_host, site_instance); } } - scoped_refptr instance(site_instance); - // Proxies are created in the FrameTree in response to a node navigating to a // new SiteInstance. Since |source|'s navigation will replace the currently // loaded document, the entire subtree under |source| will be removed. - ForEach(base::Bind(&CreateProxyForSiteInstance, instance), source); + for (FrameTreeNode* node : NodesExcept(source)) { + // If a new frame is created in the current SiteInstance, other frames in + // that SiteInstance don't need a proxy for the new frame. + SiteInstance* current_instance = + node->render_manager()->current_frame_host()->GetSiteInstance(); + if (current_instance != site_instance) + node->render_manager()->CreateRenderFrameProxy(site_instance); + } } RenderFrameHostImpl* FrameTree::GetMainFrame() const { @@ -279,8 +251,8 @@ void FrameTree::SetFocusedFrame(FrameTreeNode* node, SiteInstance* source) { if (node == GetFocusedFrame()) return; - std::set frame_tree_site_instances; - ForEach(base::Bind(&CollectSiteInstances, &frame_tree_site_instances)); + std::set frame_tree_site_instances = + CollectSiteInstances(this); SiteInstance* current_instance = node->current_frame_host()->GetSiteInstance(); @@ -311,6 +283,11 @@ void FrameTree::SetFocusedFrame(FrameTreeNode* node, SiteInstance* source) { focused_frame_tree_node_id_ = node->frame_tree_node_id(); node->DidFocus(); + + // The accessibility tree data for the root of the frame tree keeps + // track of the focused frame too, so update that every time the + // focused frame changes. + root()->current_frame_host()->UpdateAXTreeData(); } void FrameTree::SetFrameRemoveListener( @@ -333,7 +310,7 @@ RenderViewHostImpl* FrameTree::CreateRenderViewHost( // SiteInstance. Note that if swapped-out is forbidden, the // RenderViewHost's main frame has already been cleared, so we cannot rely // on checking whether the main frame is pending deletion. - if (iter->second->is_pending_deletion()) { + if (root_->render_manager()->IsViewPendingDeletion(iter->second)) { render_view_host_pending_shutdown_map_.insert( std::make_pair(site_instance->GetId(), iter->second)); render_view_host_map_.erase(iter); @@ -353,9 +330,12 @@ RenderViewHostImpl* FrameTree::CreateRenderViewHost( RenderViewHostImpl* FrameTree::GetRenderViewHost(SiteInstance* site_instance) { RenderViewHostMap::iterator iter = render_view_host_map_.find(site_instance->GetId()); - if (iter == render_view_host_map_.end()) - return nullptr; - return iter->second; + // Don't return the RVH if it is pending deletion. + if (iter != render_view_host_map_.end() && + !root_->render_manager()->IsViewPendingDeletion(iter->second)) { + return iter->second; + } + return nullptr; } void FrameTree::AddRenderViewHostRef(RenderViewHostImpl* render_view_host) { @@ -428,7 +408,16 @@ void FrameTree::UpdateLoadProgress() { double progress = 0.0; int frame_count = 0; - ForEach(base::Bind(&CollectLoadProgress, &progress, &frame_count)); + for (FrameTreeNode* node : Nodes()) { + // Ignore the current frame if it has not started loading. + if (!node->has_started_loading()) + continue; + + // Collect progress. + progress += node->loading_progress(); + frame_count++; + } + if (frame_count != 0) progress /= frame_count; @@ -441,19 +430,22 @@ void FrameTree::UpdateLoadProgress() { } void FrameTree::ResetLoadProgress() { - ForEach(base::Bind(&ResetNodeLoadProgress)); + for (FrameTreeNode* node : Nodes()) + node->reset_loading_progress(); load_progress_ = 0.0; } -bool FrameTree::IsLoading() { - bool is_loading = false; - ForEach(base::Bind(&IsNodeLoading, &is_loading)); - return is_loading; +bool FrameTree::IsLoading() const { + for (const FrameTreeNode* node : const_cast(this)->Nodes()) { + if (node->IsLoading()) + return true; + } + return false; } void FrameTree::ReplicatePageFocus(bool is_focused) { - std::set frame_tree_site_instances; - ForEach(base::Bind(&CollectSiteInstances, &frame_tree_site_instances)); + std::set frame_tree_site_instances = + CollectSiteInstances(this); // Send the focus update to main frame's proxies in all SiteInstances of // other frames in this FrameTree. Note that the main frame might also know diff --git a/chromium/content/browser/frame_host/frame_tree.h b/chromium/content/browser/frame_host/frame_tree.h index 6fc21f61cb0..840228d412d 100644 --- a/chromium/content/browser/frame_host/frame_tree.h +++ b/chromium/content/browser/frame_host/frame_tree.h @@ -7,6 +7,7 @@ #include +#include #include #include "base/callback.h" @@ -18,7 +19,6 @@ namespace content { -class FrameTreeNode; class Navigator; class RenderFrameHostDelegate; class RenderProcessHost; @@ -41,6 +41,45 @@ class RenderWidgetHostDelegate; // This object is only used on the UI thread. class CONTENT_EXPORT FrameTree { public: + class NodeRange; + + class CONTENT_EXPORT NodeIterator + : public std::iterator { + public: + NodeIterator(const NodeIterator& other); + ~NodeIterator(); + + NodeIterator& operator++(); + + bool operator==(const NodeIterator& rhs) const; + bool operator!=(const NodeIterator& rhs) const { return !(*this == rhs); } + + FrameTreeNode* operator*() { return current_node_; } + + private: + friend class NodeRange; + + NodeIterator(FrameTreeNode* starting_node, FrameTreeNode* node_to_skip); + + FrameTreeNode* current_node_; + FrameTreeNode* const node_to_skip_; + std::queue queue_; + }; + + class CONTENT_EXPORT NodeRange { + public: + NodeIterator begin(); + NodeIterator end(); + + private: + friend class FrameTree; + + NodeRange(FrameTreeNode* root, FrameTreeNode* node_to_skip); + + FrameTreeNode* const root_; + FrameTreeNode* const node_to_skip_; + }; + // Each FrameTreeNode will default to using the given |navigator| for // navigation tasks in the frame. // A set of delegates are remembered here so that we can create @@ -69,12 +108,13 @@ class CONTENT_EXPORT FrameTree { // nor searching other FrameTrees (unlike blink::WebView::findFrameByName). FrameTreeNode* FindByName(const std::string& name); - // Executes |on_node| on each node in the frame tree. If |on_node| returns - // false, terminates the iteration immediately. Returning false is useful - // if |on_node| is just doing a search over the tree. The iteration proceeds - // top-down and visits a node before adding its children to the queue, making - // it safe to remove children during the callback. - void ForEach(const base::Callback& on_node) const; + // Returns a range to iterate over all FrameTreeNodes in the frame tree in + // breadth-first traversal order. + NodeRange Nodes(); + + // Returns a range to iterate over all FrameTreeNodes in a subtree of the + // frame tree, starting from |subtree_root|. + NodeRange SubtreeNodes(FrameTreeNode* subtree_root); // Adds a new child frame to the frame tree. |process_id| is required to // disambiguate |new_routing_id|, and it must match the process of the @@ -84,6 +124,7 @@ class CONTENT_EXPORT FrameTree { int new_routing_id, blink::WebTreeScopeType scope, const std::string& frame_name, + const std::string& frame_unique_name, blink::WebSandboxFlags sandbox_flags, const blink::WebFrameOwnerProperties& frame_owner_properties); @@ -154,7 +195,7 @@ class CONTENT_EXPORT FrameTree { void ResetLoadProgress(); // Returns true if at least one of the nodes in this FrameTree is loading. - bool IsLoading(); + bool IsLoading() const; // Set page-level focus in all SiteInstances involved in rendering // this FrameTree, not including the current main frame's @@ -167,14 +208,15 @@ class CONTENT_EXPORT FrameTree { void SetPageFocus(SiteInstance* instance, bool is_focused); private: + friend class FrameTreeTest; FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest, RemoveFocusedFrame); typedef base::hash_map RenderViewHostMap; typedef std::multimap RenderViewHostMultiMap; - // A variation to the public ForEach method with a difference that the subtree - // starting at |skip_this_subtree| will not be recursed into. - void ForEach(const base::Callback& on_node, - FrameTreeNode* skip_this_subtree) const; + // Returns a range to iterate over all FrameTreeNodes in the frame tree in + // breadth-first traversal order, skipping the subtree rooted at + // |node_to_skip|. + NodeRange NodesExcept(FrameTreeNode* node_to_skip); // These delegates are installed into all the RenderViewHosts and // RenderFrameHosts that we create. diff --git a/chromium/content/browser/frame_host/frame_tree_browsertest.cc b/chromium/content/browser/frame_host/frame_tree_browsertest.cc index 1ffdd0a21d2..782234f5a02 100644 --- a/chromium/content/browser/frame_host/frame_tree_browsertest.cc +++ b/chromium/content/browser/frame_host/frame_tree_browsertest.cc @@ -464,30 +464,29 @@ IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) { // which resets both SandboxFlags::Scripts and // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and // third frame has "allow-scripts allow-same-origin". - EXPECT_EQ(root->current_replication_state().sandbox_flags, - blink::WebSandboxFlags::None); - EXPECT_EQ(root->child_at(0)->current_replication_state().sandbox_flags, - blink::WebSandboxFlags::All); - EXPECT_EQ(root->child_at(1)->current_replication_state().sandbox_flags, - blink::WebSandboxFlags::All & ~blink::WebSandboxFlags::Scripts & - ~blink::WebSandboxFlags::AutomaticFeatures); - EXPECT_EQ(root->child_at(2)->current_replication_state().sandbox_flags, - blink::WebSandboxFlags::All & ~blink::WebSandboxFlags::Scripts & + EXPECT_EQ(blink::WebSandboxFlags::None, root->effective_sandbox_flags()); + EXPECT_EQ(blink::WebSandboxFlags::All, + root->child_at(0)->effective_sandbox_flags()); + EXPECT_EQ(blink::WebSandboxFlags::All & ~blink::WebSandboxFlags::Scripts & + ~blink::WebSandboxFlags::AutomaticFeatures, + root->child_at(1)->effective_sandbox_flags()); + EXPECT_EQ(blink::WebSandboxFlags::All & ~blink::WebSandboxFlags::Scripts & ~blink::WebSandboxFlags::AutomaticFeatures & - ~blink::WebSandboxFlags::Origin); + ~blink::WebSandboxFlags::Origin, + root->child_at(2)->effective_sandbox_flags()); // Sandboxed frames should set a unique origin unless they have the // "allow-same-origin" directive. - EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null"); - EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null"); - EXPECT_EQ(root->child_at(2)->current_origin().Serialize() + "/", - main_url.GetOrigin().spec()); + EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize()); + EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize()); + EXPECT_EQ(main_url.GetOrigin().spec(), + root->child_at(2)->current_origin().Serialize() + "/"); // Navigating to a different URL should not clear sandbox flags. GURL frame_url(embedded_test_server()->GetURL("/title1.html")); NavigateFrameToURL(root->child_at(0), frame_url); - EXPECT_EQ(root->child_at(0)->current_replication_state().sandbox_flags, - blink::WebSandboxFlags::All); + EXPECT_EQ(blink::WebSandboxFlags::All, + root->child_at(0)->effective_sandbox_flags()); } // Ensure that a popup opened from a subframe sets its opener to the subframe's diff --git a/chromium/content/browser/frame_host/frame_tree_node.cc b/chromium/content/browser/frame_host/frame_tree_node.cc index b975a051106..7f1d2f57ad9 100644 --- a/chromium/content/browser/frame_host/frame_tree_node.cc +++ b/chromium/content/browser/frame_host/frame_tree_node.cc @@ -14,11 +14,13 @@ #include "content/browser/frame_host/navigation_request.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/frame_host/traced_frame_tree_node.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/common/frame_messages.h" #include "content/common/site_isolation_policy.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/browser_side_navigation_policy.h" +#include "third_party/WebKit/public/web/WebSandboxFlags.h" namespace content { @@ -77,7 +79,7 @@ FrameTreeNode::FrameTreeNode( RenderFrameHostManager::Delegate* manager_delegate, blink::WebTreeScopeType scope, const std::string& name, - blink::WebSandboxFlags sandbox_flags, + const std::string& unique_name, const blink::WebFrameOwnerProperties& frame_owner_properties) : frame_tree_(frame_tree), navigator_(navigator), @@ -94,17 +96,23 @@ FrameTreeNode::FrameTreeNode( replication_state_( scope, name, - sandbox_flags, - false /* should enforce strict mixed content checking */), - // Effective sandbox flags also need to be set, since initial sandbox - // flags should apply to the initial empty document in the frame. - effective_sandbox_flags_(sandbox_flags), + unique_name, + blink::WebSandboxFlags::None, + false /* should enforce strict mixed content checking */, + false /* is a potentially trustworthy unique origin */), + pending_sandbox_flags_(blink::WebSandboxFlags::None), frame_owner_properties_(frame_owner_properties), loading_progress_(kLoadingProgressNotStarted) { std::pair result = g_frame_tree_node_id_map.Get().insert( std::make_pair(frame_tree_node_id_, this)); CHECK(result.second); + + TRACE_EVENT_OBJECT_CREATED_WITH_ID( + "navigation", "FrameTreeNode", + TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_)); + // Don't TraceSnapshot() until the RenderFrameHostManager is initialized and + // calls SetCurrentURL(). } FrameTreeNode::~FrameTreeNode() { @@ -116,6 +124,10 @@ FrameTreeNode::~FrameTreeNode() { opener_->RemoveObserver(opener_observer_.get()); g_frame_tree_node_id_map.Get().erase(frame_tree_node_id_); + + TRACE_EVENT_OBJECT_DELETED_WITH_ID( + "navigation", "FrameTreeNode", + TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_)); } void FrameTreeNode::AddObserver(Observer* observer) { @@ -171,7 +183,8 @@ void FrameTreeNode::RemoveChild(FrameTreeNode* child) { } void FrameTreeNode::ResetForNewProcess() { - current_url_ = GURL(); + current_frame_host()->set_last_committed_url(GURL()); + TraceSnapshot(); // Remove child nodes from the tree, then delete them. This destruction // operation will notify observers. @@ -196,19 +209,34 @@ void FrameTreeNode::SetOpener(FrameTreeNode* opener) { void FrameTreeNode::SetCurrentURL(const GURL& url) { if (!has_committed_real_load_ && url != GURL(url::kAboutBlankURL)) has_committed_real_load_ = true; - current_url_ = url; + current_frame_host()->set_last_committed_url(url); + TraceSnapshot(); } -void FrameTreeNode::SetCurrentOrigin(const url::Origin& origin) { - if (!origin.IsSameOriginWith(replication_state_.origin)) - render_manager_.OnDidUpdateOrigin(origin); +void FrameTreeNode::SetCurrentOrigin( + const url::Origin& origin, + bool is_potentially_trustworthy_unique_origin) { + if (!origin.IsSameOriginWith(replication_state_.origin) || + replication_state_.has_potentially_trustworthy_unique_origin != + is_potentially_trustworthy_unique_origin) { + render_manager_.OnDidUpdateOrigin(origin, + is_potentially_trustworthy_unique_origin); + } replication_state_.origin = origin; + replication_state_.has_potentially_trustworthy_unique_origin = + is_potentially_trustworthy_unique_origin; } -void FrameTreeNode::SetFrameName(const std::string& name) { - if (name != replication_state_.name) - render_manager_.OnDidUpdateName(name); +void FrameTreeNode::SetFrameName(const std::string& name, + const std::string& unique_name) { + if (name == replication_state_.name) { + // |unique_name| shouldn't change unless |name| changes. + DCHECK_EQ(unique_name, replication_state_.unique_name); + return; + } + render_manager_.OnDidUpdateName(name, unique_name); replication_state_.name = name; + replication_state_.unique_name = unique_name; } void FrameTreeNode::SetEnforceStrictMixedContentChecking(bool should_enforce) { @@ -221,6 +249,15 @@ void FrameTreeNode::SetEnforceStrictMixedContentChecking(bool should_enforce) { should_enforce; } +void FrameTreeNode::SetPendingSandboxFlags( + blink::WebSandboxFlags sandbox_flags) { + pending_sandbox_flags_ = sandbox_flags; + + // Subframes should always inherit their parent's sandbox flags. + if (parent()) + pending_sandbox_flags_ |= parent()->effective_sandbox_flags(); +} + bool FrameTreeNode::IsDescendantOf(FrameTreeNode* other) const { if (!other || !other->child_count()) return false; @@ -271,48 +308,68 @@ bool FrameTreeNode::IsLoading() const { bool FrameTreeNode::CommitPendingSandboxFlags() { bool did_change_flags = - effective_sandbox_flags_ != replication_state_.sandbox_flags; - effective_sandbox_flags_ = replication_state_.sandbox_flags; + pending_sandbox_flags_ != replication_state_.sandbox_flags; + replication_state_.sandbox_flags = pending_sandbox_flags_; return did_change_flags; } void FrameTreeNode::CreatedNavigationRequest( scoped_ptr navigation_request) { CHECK(IsBrowserSideNavigationEnabled()); - ResetNavigationRequest(false); + + bool was_previously_loading = frame_tree()->IsLoading(); + + // There's no need to reset the state: there's still an ongoing load, and the + // RenderFrameHostManager will take care of updates to the speculative + // RenderFrameHost in DidCreateNavigationRequest below. + if (was_previously_loading) + ResetNavigationRequest(true); + + navigation_request_ = std::move(navigation_request); + render_manager()->DidCreateNavigationRequest(navigation_request_.get()); // Force the throbber to start to keep it in sync with what is happening in // the UI. Blink doesn't send throb notifications for JavaScript URLs, so it // is not done here either. - if (!navigation_request->common_params().url.SchemeIs( + if (!navigation_request_->common_params().url.SchemeIs( url::kJavaScriptScheme)) { // TODO(fdegans): Check if this is a same-document navigation and set the // proper argument. - DidStartLoading(true); + DidStartLoading(true, was_previously_loading); } - - navigation_request_ = std::move(navigation_request); - - render_manager()->DidCreateNavigationRequest(*navigation_request_); } -void FrameTreeNode::ResetNavigationRequest(bool is_commit) { +void FrameTreeNode::ResetNavigationRequest(bool keep_state) { CHECK(IsBrowserSideNavigationEnabled()); if (!navigation_request_) return; + bool was_renderer_initiated = !navigation_request_->browser_initiated(); + NavigationRequest::AssociatedSiteInstanceType site_instance_type = + navigation_request_->associated_site_instance_type(); navigation_request_.reset(); - // During commit, the clean up of a speculative RenderFrameHost is done in - // RenderFrameHostManager::DidNavigateFrame. The load is also still being - // tracked. - if (is_commit) + if (keep_state) return; - // If the reset corresponds to a cancelation, the RenderFrameHostManager - // should clean up any speculative RenderFrameHost it created for the - // navigation. + // The RenderFrameHostManager should clean up any speculative RenderFrameHost + // it created for the navigation. Also register that the load stopped. DidStopLoading(); render_manager_.CleanUpNavigation(); + + // When reusing the same SiteInstance, a pending WebUI may have been created + // on behalf of the navigation in the current RenderFrameHost. Clear it. + if (site_instance_type == + NavigationRequest::AssociatedSiteInstanceType::CURRENT) { + current_frame_host()->ClearPendingWebUI(); + } + + // If the navigation is renderer-initiated, the renderer should also be + // informed that the navigation stopped. + if (was_renderer_initiated) { + current_frame_host()->Send( + new FrameMsg_Stop(current_frame_host()->GetRoutingID())); + } + } bool FrameTreeNode::has_started_loading() const { @@ -323,7 +380,8 @@ void FrameTreeNode::reset_loading_progress() { loading_progress_ = kLoadingProgressNotStarted; } -void FrameTreeNode::DidStartLoading(bool to_different_document) { +void FrameTreeNode::DidStartLoading(bool to_different_document, + bool was_previously_loading) { // Any main frame load to a new document should reset the load progress since // it will replace the current page and any frames. The WebContents will // be notified when DidChangeLoadProgress is called. @@ -331,7 +389,7 @@ void FrameTreeNode::DidStartLoading(bool to_different_document) { frame_tree_->ResetLoadProgress(); // Notify the WebContents. - if (!frame_tree_->IsLoading()) + if (!was_previously_loading) navigator()->GetDelegate()->DidStartLoading(this, to_different_document); // Set initial load progress and update overall progress. This will notify @@ -398,4 +456,36 @@ void FrameTreeNode::DidFocus() { FOR_EACH_OBSERVER(Observer, observers_, OnFrameTreeNodeFocused(this)); } +void FrameTreeNode::BeforeUnloadCanceled() { + // TODO(clamy): Support BeforeUnload in subframes. + if (!IsMainFrame()) + return; + + RenderFrameHostImpl* current_frame_host = + render_manager_.current_frame_host(); + DCHECK(current_frame_host); + current_frame_host->ResetLoadingState(); + + if (IsBrowserSideNavigationEnabled()) { + RenderFrameHostImpl* speculative_frame_host = + render_manager_.speculative_frame_host(); + if (speculative_frame_host) + speculative_frame_host->ResetLoadingState(); + } else { + RenderFrameHostImpl* pending_frame_host = + render_manager_.pending_frame_host(); + if (pending_frame_host) + pending_frame_host->ResetLoadingState(); + } +} + +void FrameTreeNode::TraceSnapshot() const { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( + "navigation", "FrameTreeNode", + TRACE_ID_WITH_SCOPE("FrameTreeNode", frame_tree_node_id_), + scoped_ptr( + new TracedFrameTreeNode(*this))); +} + } // namespace content diff --git a/chromium/content/browser/frame_host/frame_tree_node.h b/chromium/content/browser/frame_host/frame_tree_node.h index 9103b63aa70..044764b9144 100644 --- a/chromium/content/browser/frame_host/frame_tree_node.h +++ b/chromium/content/browser/frame_host/frame_tree_node.h @@ -51,6 +51,8 @@ class CONTENT_EXPORT FrameTreeNode { // regardless of which FrameTree it is in. static FrameTreeNode* GloballyFindByID(int frame_tree_node_id); + // Callers are are expected to initialize sandbox flags separately after + // calling the constructor. FrameTreeNode(FrameTree* frame_tree, Navigator* navigator, RenderFrameHostDelegate* render_frame_delegate, @@ -59,7 +61,7 @@ class CONTENT_EXPORT FrameTreeNode { RenderFrameHostManager::Delegate* manager_delegate, blink::WebTreeScopeType scope, const std::string& name, - blink::WebSandboxFlags sandbox_flags, + const std::string& unique_name, const blink::WebFrameOwnerProperties& frame_owner_properties); ~FrameTreeNode(); @@ -97,8 +99,6 @@ class CONTENT_EXPORT FrameTreeNode { return replication_state_.name; } - const url::Origin& frame_origin() const { return replication_state_.origin; } - size_t child_count() const { return children_.size(); } @@ -116,9 +116,9 @@ class CONTENT_EXPORT FrameTreeNode { return children_[index].get(); } - // Returns the URL of the last committed page in this frame. + // Returns the URL of the last committed page in the current frame. const GURL& current_url() const { - return current_url_; + return current_frame_host()->last_committed_url(); } // Sets the last committed URL for this frame and updates @@ -136,25 +136,43 @@ class CONTENT_EXPORT FrameTreeNode { } // Set the current origin and notify proxies about the update. - void SetCurrentOrigin(const url::Origin& origin); + void SetCurrentOrigin(const url::Origin& origin, + bool is_potentially_trustworthy_unique_origin); // Set the current name and notify proxies about the update. - void SetFrameName(const std::string& name); + void SetFrameName(const std::string& name, const std::string& unique_name); // Sets the current enforcement of strict mixed content checking and // notifies proxies about the update. void SetEnforceStrictMixedContentChecking(bool should_enforce); - blink::WebSandboxFlags effective_sandbox_flags() { - return effective_sandbox_flags_; + // Returns the currently active sandbox flags for this frame. This includes + // flags inherited from parent frames and the currently active flags from the + // "); + FrameReplicationState frame_replication_state; + frame_replication_state.name = "frame"; + frame_replication_state.unique_name = "frame-uniqueName"; + RenderFrameImpl::FromWebFrame( view_->GetMainRenderFrame()->GetWebFrame()->firstChild()) - ->OnSwapOut(kFrameProxyRouteId, false, FrameReplicationState()); + ->OnSwapOut(kFrameProxyRouteId, false, frame_replication_state); - RenderFrameImpl::CreateFrame(kSubframeRouteId, MSG_ROUTING_NONE, - MSG_ROUTING_NONE, kFrameProxyRouteId, - MSG_ROUTING_NONE, FrameReplicationState(), - &compositor_deps_, widget_params, - blink::WebFrameOwnerProperties()); + RenderFrameImpl::CreateFrame( + kSubframeRouteId, MSG_ROUTING_NONE, MSG_ROUTING_NONE, + kFrameProxyRouteId, MSG_ROUTING_NONE, frame_replication_state, + &compositor_deps_, widget_params, blink::WebFrameOwnerProperties()); frame_ = RenderFrameImpl::FromRoutingID(kSubframeRouteId); EXPECT_FALSE(frame_->is_main_frame_); @@ -71,6 +76,14 @@ class RenderFrameImplTest : public RenderViewTest { RenderViewTest::TearDown(); } + void SetIsUsingLoFi(RenderFrameImpl* frame, bool is_using_lofi) { + frame->is_using_lofi_ = is_using_lofi; + } + + RenderFrameImpl* GetMainRenderFrame() { + return static_cast(view_->GetMainRenderFrame()); + } + RenderFrameImpl* frame() { return frame_; } content::RenderWidget* frame_widget() const { @@ -115,26 +128,13 @@ class RenderFrameTestObserver : public RenderFrameObserver { // RenderWidget. TEST_F(RenderFrameImplTest, MAYBE_SubframeWidget) { EXPECT_TRUE(frame_widget()); - // We can't convert to RenderWidget* directly, because - // it and RenderView are two unrelated base classes - // of RenderViewImpl. If a class has multiple base classes, - // each base class pointer will be distinct, and direct casts - // between unrelated base classes are undefined, even if they share - // a common derived class. The compiler has no way in general of - // determining the displacement between the two classes, so these - // types of casts cannot be implemented in a type safe way. - // To overcome this, we make two legal static casts: - // first, downcast from RenderView* to RenderViewImpl*, - // then upcast from RenderViewImpl* to RenderWidget*. - EXPECT_NE(frame_widget(), - static_cast( - static_cast((view_)))); + EXPECT_NE(frame_widget(), view_->GetWidget()); } // Verify a subframe RenderWidget properly processes its viewport being // resized. TEST_F(RenderFrameImplTest, MAYBE_FrameResize) { - ViewMsg_Resize_Params resize_params; + ResizeParams resize_params; gfx::Size size(200, 200); resize_params.screen_info = blink::WebScreenInfo(); resize_params.new_size = size; @@ -177,4 +177,50 @@ TEST_F(RenderFrameImplTest, MAYBE_FrameWasShownAfterWidgetClose) { EXPECT_TRUE(observer.visible()); } +// Test that LoFi state only updates for new main frame documents. Subframes +// inherit from the main frame and should not change at commit time. +TEST_F(RenderFrameImplTest, LoFiNotUpdatedOnSubframeCommits) { + SetIsUsingLoFi(GetMainRenderFrame(), true); + SetIsUsingLoFi(frame(), true); + EXPECT_TRUE(GetMainRenderFrame()->IsUsingLoFi()); + EXPECT_TRUE(frame()->IsUsingLoFi()); + + blink::WebHistoryItem item; + item.initialize(); + + // The main frame's and subframe's LoFi states should stay the same on + // navigations within the page. + frame()->didNavigateWithinPage(frame()->GetWebFrame(), item, + blink::WebStandardCommit); + EXPECT_TRUE(frame()->IsUsingLoFi()); + GetMainRenderFrame()->didNavigateWithinPage( + GetMainRenderFrame()->GetWebFrame(), item, blink::WebStandardCommit); + EXPECT_TRUE(GetMainRenderFrame()->IsUsingLoFi()); + + // The subframe's LoFi state should not be reset on commit. + DocumentState* document_state = + DocumentState::FromDataSource(frame()->GetWebFrame()->dataSource()); + static_cast(document_state->navigation_state()) + ->set_was_within_same_page(false); + + frame()->didCommitProvisionalLoad(frame()->GetWebFrame(), item, + blink::WebStandardCommit); + EXPECT_TRUE(frame()->IsUsingLoFi()); + + // The main frame's LoFi state should be reset to off on commit. + document_state = DocumentState::FromDataSource( + GetMainRenderFrame()->GetWebFrame()->dataSource()); + static_cast(document_state->navigation_state()) + ->set_was_within_same_page(false); + + // Calling didCommitProvisionalLoad is not representative of a full navigation + // but serves the purpose of testing the LoFi state logic. + GetMainRenderFrame()->didCommitProvisionalLoad( + GetMainRenderFrame()->GetWebFrame(), item, blink::WebStandardCommit); + EXPECT_FALSE(GetMainRenderFrame()->IsUsingLoFi()); + // The subframe would be deleted here after a cross-document navigation. It + // happens to be left around in this test because this does not simulate the + // frame detach. +} + } // namespace diff --git a/chromium/content/renderer/render_frame_proxy.cc b/chromium/content/renderer/render_frame_proxy.cc index 39a0d348083..37b7059f289 100644 --- a/chromium/content/renderer/render_frame_proxy.cc +++ b/chromium/content/renderer/render_frame_proxy.cc @@ -14,6 +14,7 @@ #include "content/common/frame_messages.h" #include "content/common/frame_replication_state.h" #include "content/common/input_messages.h" +#include "content/common/page_messages.h" #include "content/common/site_isolation_policy.h" #include "content/common/swapped_out_messages.h" #include "content/common/view_messages.h" @@ -21,6 +22,9 @@ #include "content/renderer/render_frame_impl.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/render_view_impl.h" +#include "content/renderer/render_widget.h" +#include "ipc/ipc_message_macros.h" +#include "third_party/WebKit/public/platform/URLConversion.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" @@ -56,7 +60,18 @@ RenderFrameProxy* RenderFrameProxy::CreateProxyToReplaceFrame( // follow later. blink::WebRemoteFrame* web_frame = blink::WebRemoteFrame::create(scope, proxy.get()); - proxy->Init(web_frame, frame_to_replace->render_view()); + + // If frame_to_replace has a RenderFrameProxy parent, then its + // RenderWidget will be destroyed along with it, so the new + // RenderFrameProxy uses its parent's RenderWidget. + RenderWidget* widget = + (!frame_to_replace->GetWebFrame()->parent() || + frame_to_replace->GetWebFrame()->parent()->isWebLocalFrame()) + ? frame_to_replace->GetRenderWidget() + : RenderFrameProxy::FromWebFrame( + frame_to_replace->GetWebFrame()->parent()) + ->render_widget(); + proxy->Init(web_frame, frame_to_replace->render_view(), widget); return proxy.release(); } @@ -76,17 +91,22 @@ RenderFrameProxy* RenderFrameProxy::CreateFrameProxy( return nullptr; } + blink::WebFrame* opener = + RenderFrameImpl::ResolveOpener(opener_routing_id, nullptr); + scoped_ptr proxy( new RenderFrameProxy(routing_id, MSG_ROUTING_NONE)); - RenderViewImpl* render_view = NULL; - blink::WebRemoteFrame* web_frame = NULL; + RenderViewImpl* render_view = nullptr; + RenderWidget* render_widget = nullptr; + blink::WebRemoteFrame* web_frame = nullptr; if (!parent) { // Create a top level WebRemoteFrame. render_view = RenderViewImpl::FromRoutingID(render_view_routing_id); - web_frame = - blink::WebRemoteFrame::create(replicated_state.scope, proxy.get()); + web_frame = blink::WebRemoteFrame::create(replicated_state.scope, + proxy.get(), opener); render_view->webview()->setMainFrame(web_frame); + render_widget = render_view->GetWidget(); } else { // Create a frame under an existing parent. The parent is always expected // to be a RenderFrameProxy, because navigations initiated by local frames @@ -95,15 +115,13 @@ RenderFrameProxy* RenderFrameProxy::CreateFrameProxy( web_frame = parent->web_frame()->createRemoteChild( replicated_state.scope, blink::WebString::fromUTF8(replicated_state.name), - replicated_state.sandbox_flags, proxy.get()); + blink::WebString::fromUTF8(replicated_state.unique_name), + replicated_state.sandbox_flags, proxy.get(), opener); render_view = parent->render_view(); + render_widget = parent->render_widget(); } - blink::WebFrame* opener = - RenderFrameImpl::ResolveOpener(opener_routing_id, nullptr); - web_frame->setOpener(opener); - - proxy->Init(web_frame, render_view); + proxy->Init(web_frame, render_view, render_widget); // Initialize proxy's WebRemoteFrame with the security origin and other // replicated information. @@ -138,8 +156,9 @@ RenderFrameProxy* RenderFrameProxy::FromWebFrame(blink::WebFrame* web_frame) { RenderFrameProxy::RenderFrameProxy(int routing_id, int frame_routing_id) : routing_id_(routing_id), frame_routing_id_(frame_routing_id), - web_frame_(NULL), - render_view_(NULL) { + web_frame_(nullptr), + render_view_(nullptr), + render_widget_(nullptr) { std::pair result = g_routing_id_proxy_map.Get().insert(std::make_pair(routing_id_, this)); CHECK(result.second) << "Inserting a duplicate item."; @@ -156,7 +175,7 @@ RenderFrameProxy::~RenderFrameProxy() { if (render_frame) render_frame->set_render_frame_proxy(nullptr); - render_view()->UnregisterRenderFrameProxy(this); + render_widget_->UnregisterRenderFrameProxy(this); CHECK(!web_frame_); RenderThread::Get()->RemoveRoute(routing_id_); @@ -164,38 +183,47 @@ RenderFrameProxy::~RenderFrameProxy() { } void RenderFrameProxy::Init(blink::WebRemoteFrame* web_frame, - RenderViewImpl* render_view) { + RenderViewImpl* render_view, + RenderWidget* render_widget) { CHECK(web_frame); CHECK(render_view); + CHECK(render_widget); web_frame_ = web_frame; render_view_ = render_view; + render_widget_ = render_widget; - // TODO(nick): Should all RenderFrameProxies remain observers of their views? - render_view_->RegisterRenderFrameProxy(this); + render_widget_->RegisterRenderFrameProxy(this); std::pair result = g_frame_map.Get().insert(std::make_pair(web_frame_, this)); CHECK(result.second) << "Inserted a duplicate item."; } -bool RenderFrameProxy::IsMainFrameDetachedFromTree() const { - return web_frame_->top() == web_frame_ && - render_view_->webview()->mainFrame()->isWebLocalFrame(); +void RenderFrameProxy::WillBeginCompositorFrame() { + if (compositing_helper_) { + FrameHostMsg_HittestData_Params params; + params.surface_id = compositing_helper_->surface_id(); + params.ignored_for_hittest = web_frame_->isIgnoredForHitTest(); + render_widget_->QueueMessage( + new FrameHostMsg_HittestData(render_widget_->routing_id(), params), + MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE); + } } void RenderFrameProxy::DidCommitCompositorFrame() { - if (compositing_helper_.get()) - compositing_helper_->DidCommitCompositorFrame(); } void RenderFrameProxy::SetReplicatedState(const FrameReplicationState& state) { DCHECK(web_frame_); web_frame_->setReplicatedOrigin(state.origin); web_frame_->setReplicatedSandboxFlags(state.sandbox_flags); - web_frame_->setReplicatedName(blink::WebString::fromUTF8(state.name)); + web_frame_->setReplicatedName(blink::WebString::fromUTF8(state.name), + blink::WebString::fromUTF8(state.unique_name)); web_frame_->setReplicatedShouldEnforceStrictMixedContentChecking( state.should_enforce_strict_mixed_content_checking); + web_frame_->setReplicatedPotentiallyTrustworthyUniqueOrigin( + state.has_potentially_trustworthy_unique_origin); } // Update the proxy's SecurityContext and FrameOwner with new sandbox flags @@ -220,12 +248,18 @@ void RenderFrameProxy::OnDidUpdateSandboxFlags(blink::WebSandboxFlags flags) { } bool RenderFrameProxy::OnMessageReceived(const IPC::Message& msg) { + // Forward Page IPCs to the RenderView. + if ((IPC_MESSAGE_CLASS(msg) == PageMsgStart)) { + if (render_view()) + return render_view()->OnMessageReceived(msg); + + return false; + } + bool handled = true; IPC_BEGIN_MESSAGE_MAP(RenderFrameProxy, msg) IPC_MESSAGE_HANDLER(FrameMsg_DeleteProxy, OnDeleteProxy) IPC_MESSAGE_HANDLER(FrameMsg_ChildFrameProcessGone, OnChildFrameProcessGone) - IPC_MESSAGE_HANDLER_GENERIC(FrameMsg_CompositorFrameSwapped, - OnCompositorFrameSwapped(msg)) IPC_MESSAGE_HANDLER(FrameMsg_SetChildFrameSurface, OnSetChildFrameSurface) IPC_MESSAGE_HANDLER(FrameMsg_UpdateOpener, OnUpdateOpener) IPC_MESSAGE_HANDLER(FrameMsg_DidStartLoading, OnDidStartLoading) @@ -250,7 +284,7 @@ bool RenderFrameProxy::Send(IPC::Message* message) { } void RenderFrameProxy::OnDeleteProxy() { - DCHECK(web_frame_->isWebRemoteFrame()); + DCHECK(web_frame_); web_frame_->detach(); } @@ -259,32 +293,6 @@ void RenderFrameProxy::OnChildFrameProcessGone() { compositing_helper_->ChildFrameGone(); } -void RenderFrameProxy::OnCompositorFrameSwapped(const IPC::Message& message) { - // If this WebFrame has already been detached, its parent will be null. This - // can happen when swapping a WebRemoteFrame with a WebLocalFrame, where this - // message may arrive after the frame was removed from the frame tree, but - // before the frame has been destroyed. http://crbug.com/446575. - if (!web_frame()->parent()) - return; - - FrameMsg_CompositorFrameSwapped::Param param; - if (!FrameMsg_CompositorFrameSwapped::Read(&message, ¶m)) - return; - - scoped_ptr frame(new cc::CompositorFrame); - base::get<0>(param).frame.AssignTo(frame.get()); - - if (!compositing_helper_.get()) { - compositing_helper_ = - ChildFrameCompositingHelper::CreateForRenderFrameProxy(this); - } - compositing_helper_->OnCompositorFrameSwapped( - std::move(frame), base::get<0>(param).producing_route_id, - base::get<0>(param).output_surface_id, - base::get<0>(param).producing_host_id, - base::get<0>(param).shared_memory_handle); -} - void RenderFrameProxy::OnSetChildFrameSurface( const cc::SurfaceId& surface_id, const gfx::Size& frame_size, @@ -308,32 +316,14 @@ void RenderFrameProxy::OnSetChildFrameSurface( void RenderFrameProxy::OnUpdateOpener(int opener_routing_id) { blink::WebFrame* opener = RenderFrameImpl::ResolveOpener(opener_routing_id, nullptr); - - // When there is a RenderFrame for this proxy, tell it to update its opener. - // TODO(alexmos, nasko): Remove this when we only have WebRemoteFrames. - if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) { - RenderFrameImpl* render_frame = - RenderFrameImpl::FromRoutingID(frame_routing_id_); - if (render_frame) { - render_frame->GetWebFrame()->setOpener(opener); - return; - } - } - web_frame_->setOpener(opener); } void RenderFrameProxy::OnDidStartLoading() { - if (IsMainFrameDetachedFromTree()) - return; - web_frame_->didStartLoading(); } void RenderFrameProxy::OnDidStopLoading() { - if (IsMainFrameDetachedFromTree()) - return; - web_frame_->didStopLoading(); } @@ -341,8 +331,10 @@ void RenderFrameProxy::OnDispatchLoad() { web_frame_->DispatchLoadEventForFrameOwner(); } -void RenderFrameProxy::OnDidUpdateName(const std::string& name) { - web_frame_->setReplicatedName(blink::WebString::fromUTF8(name)); +void RenderFrameProxy::OnDidUpdateName(const std::string& name, + const std::string& unique_name) { + web_frame_->setReplicatedName(blink::WebString::fromUTF8(name), + blink::WebString::fromUTF8(unique_name)); } void RenderFrameProxy::OnEnforceStrictMixedContentChecking( @@ -351,8 +343,12 @@ void RenderFrameProxy::OnEnforceStrictMixedContentChecking( should_enforce); } -void RenderFrameProxy::OnDidUpdateOrigin(const url::Origin& origin) { +void RenderFrameProxy::OnDidUpdateOrigin( + const url::Origin& origin, + bool is_potentially_trustworthy_unique_origin) { web_frame_->setReplicatedOrigin(origin); + web_frame_->setReplicatedPotentiallyTrustworthyUniqueOrigin( + is_potentially_trustworthy_unique_origin); } void RenderFrameProxy::OnSetPageFocus(bool is_focused) { @@ -431,7 +427,8 @@ void RenderFrameProxy::navigate(const blink::WebURLRequest& request, FrameHostMsg_OpenURL_Params params; params.url = request.url(); params.referrer = Referrer( - GURL(request.httpHeaderField(blink::WebString::fromUTF8("Referer"))), + blink::WebStringToGURL( + request.httpHeaderField(blink::WebString::fromUTF8("Referer"))), request.referrerPolicy()); params.disposition = CURRENT_TAB; params.should_replace_current_entry = should_replace_current_entry; diff --git a/chromium/content/renderer/render_frame_proxy.h b/chromium/content/renderer/render_frame_proxy.h index 1ce02646014..5fd375c6285 100644 --- a/chromium/content/renderer/render_frame_proxy.h +++ b/chromium/content/renderer/render_frame_proxy.h @@ -33,6 +33,7 @@ namespace content { class ChildFrameCompositingHelper; class RenderFrameImpl; class RenderViewImpl; +class RenderWidget; struct FrameReplicationState; // When a page's frames are rendered by multiple processes, each renderer has a @@ -102,6 +103,10 @@ class CONTENT_EXPORT RenderFrameProxy // IPC::Sender bool Send(IPC::Message* msg) override; + // Out-of-process child frames receive a signal from RenderWidgetCompositor + // when a compositor frame will begin. + void WillBeginCompositorFrame(); + // Out-of-process child frames receive a signal from RenderWidgetCompositor // when a compositor frame has committed. void DidCommitCompositorFrame(); @@ -110,16 +115,13 @@ class CONTENT_EXPORT RenderFrameProxy // RenderFrameProxy's WebRemoteFrame. void SetReplicatedState(const FrameReplicationState& state); - // Navigating a top-level frame cross-process does not swap the WebLocalFrame - // for a WebRemoteFrame in the frame tree. In this case, this WebRemoteFrame - // is not attached to the frame tree and there is no blink::Frame associated - // with it, so it is not in state where most operations on it will succeed. - bool IsMainFrameDetachedFromTree() const; - int routing_id() { return routing_id_; } RenderViewImpl* render_view() { return render_view_; } blink::WebRemoteFrame* web_frame() { return web_frame_; } + // Returns the widget used for the local frame root. + RenderWidget* render_widget() { return render_widget_; } + // blink::WebRemoteFrameClient implementation: void frameDetached(DetachType type) override; void postMessageEvent(blink::WebLocalFrame* sourceFrame, @@ -144,7 +146,9 @@ class CONTENT_EXPORT RenderFrameProxy private: RenderFrameProxy(int routing_id, int frame_routing_id); - void Init(blink::WebRemoteFrame* frame, RenderViewImpl* render_view); + void Init(blink::WebRemoteFrame* frame, + RenderViewImpl* render_view, + RenderWidget* render_widget); // IPC::Listener bool OnMessageReceived(const IPC::Message& msg) override; @@ -161,9 +165,10 @@ class CONTENT_EXPORT RenderFrameProxy void OnDidStopLoading(); void OnDidUpdateSandboxFlags(blink::WebSandboxFlags flags); void OnDispatchLoad(); - void OnDidUpdateName(const std::string& name); + void OnDidUpdateName(const std::string& name, const std::string& unique_name); void OnEnforceStrictMixedContentChecking(bool should_enforce); - void OnDidUpdateOrigin(const url::Origin& origin); + void OnDidUpdateOrigin(const url::Origin& origin, + bool is_potentially_trustworthy_unique_origin); void OnSetPageFocus(bool is_focused); void OnSetFocusedFrame(); @@ -179,6 +184,7 @@ class CONTENT_EXPORT RenderFrameProxy scoped_refptr compositing_helper_; RenderViewImpl* render_view_; + RenderWidget* render_widget_; DISALLOW_COPY_AND_ASSIGN(RenderFrameProxy); }; diff --git a/chromium/content/renderer/render_process_impl.cc b/chromium/content/renderer/render_process_impl.cc index 16737ea3166..c9d0d5325a1 100644 --- a/chromium/content/renderer/render_process_impl.cc +++ b/chromium/content/renderer/render_process_impl.cc @@ -14,6 +14,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" +#include "base/feature_list.h" #include "base/sys_info.h" #include "content/child/site_isolation_stats_gatherer.h" #include "content/public/common/content_switches.h" @@ -21,6 +22,32 @@ #include "third_party/WebKit/public/web/WebFrame.h" #include "v8/include/v8.h" +namespace { + +const base::Feature kV8_ES2015_TailCalls_Feature { + "V8_ES2015_TailCalls", base::FEATURE_DISABLED_BY_DEFAULT +}; + +const base::Feature kV8SerializeEagerFeature{"V8_Serialize_Eager", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kV8SerializeAgeCodeFeature{ + "V8_Serialize_Age_Code", base::FEATURE_DISABLED_BY_DEFAULT}; + +void SetV8FlagIfFeature(const base::Feature& feature, const char* v8_flag) { + if (base::FeatureList::IsEnabled(feature)) { + v8::V8::SetFlagsFromString(v8_flag, strlen(v8_flag)); + } +} + +void SetV8FlagIfHasSwitch(const char* switch_name, const char* v8_flag) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) { + v8::V8::SetFlagsFromString(v8_flag, strlen(v8_flag)); + } +} + +} // namespace + namespace content { RenderProcessImpl::RenderProcessImpl() @@ -42,20 +69,23 @@ RenderProcessImpl::RenderProcessImpl() } #endif - std::string scavenge_reclaim_unmodified_flag( - "--scavenge_reclaim_unmodified_objects"); - v8::V8::SetFlagsFromString( - scavenge_reclaim_unmodified_flag.c_str(), - static_cast(scavenge_reclaim_unmodified_flag.size())); - if (base::SysInfo::IsLowEndDevice()) { std::string optimize_flag("--optimize-for-size"); v8::V8::SetFlagsFromString(optimize_flag.c_str(), static_cast(optimize_flag.size())); } + SetV8FlagIfFeature(kV8_ES2015_TailCalls_Feature, "--harmony-tailcalls"); + SetV8FlagIfFeature(kV8SerializeEagerFeature, "--serialize_eager"); + SetV8FlagIfFeature(kV8SerializeAgeCodeFeature, "--serialize_age_code"); + SetV8FlagIfHasSwitch(switches::kDisableJavaScriptHarmonyShipping, + "--noharmony-shipping"); + SetV8FlagIfHasSwitch(switches::kJavaScriptHarmony, "--harmony"); + SetV8FlagIfHasSwitch(switches::kEnableWasm, "--expose-wasm"); + const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kJavaScriptFlags)) { std::string flags( command_line.GetSwitchValueASCII(switches::kJavaScriptFlags)); diff --git a/chromium/content/renderer/render_thread_impl.cc b/chromium/content/renderer/render_thread_impl.cc index dda8f7dbcfd..30047189f7c 100644 --- a/chromium/content/renderer/render_thread_impl.cc +++ b/chromium/content/renderer/render_thread_impl.cc @@ -12,6 +12,7 @@ #include "base/allocator/allocator_extension.h" #include "base/command_line.h" +#include "base/debug/crash_logging.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/logging.h" @@ -32,7 +33,6 @@ #include "base/threading/simple_thread.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" -#include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" #include "base/values.h" #include "build/build_config.h" @@ -40,7 +40,6 @@ #include "cc/base/switches.h" #include "cc/blink/web_external_bitmap_impl.h" #include "cc/blink/web_layer_impl.h" -#include "cc/layers/layer_settings.h" #include "cc/raster/task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_settings.h" @@ -50,6 +49,7 @@ #include "components/scheduler/renderer/renderer_scheduler.h" #include "content/child/appcache/appcache_dispatcher.h" #include "content/child/appcache/appcache_frontend_impl.h" +#include "content/child/blob_storage/blob_message_filter.h" #include "content/child/child_discardable_shared_memory_manager.h" #include "content/child/child_gpu_memory_buffer_manager.h" #include "content/child/child_histogram_message_filter.h" @@ -59,8 +59,6 @@ #include "content/child/db_message_filter.h" #include "content/child/indexed_db/indexed_db_dispatcher.h" #include "content/child/indexed_db/indexed_db_message_filter.h" -#include "content/child/npapi/npobject_util.h" -#include "content/child/plugin_messages.h" #include "content/child/resource_dispatcher.h" #include "content/child/resource_scheduling_filter.h" #include "content/child/runtime_features.h" @@ -73,9 +71,7 @@ #include "content/common/dom_storage/dom_storage_messages.h" #include "content/common/frame_messages.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gpu_channel_host.h" -#include "content/common/gpu/gpu_messages.h" -#include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/common/gpu_process_launch_causes.h" #include "content/common/render_frame_setup.mojom.h" #include "content/common/render_process_messages.h" #include "content/common/resource_messages.h" @@ -130,6 +126,7 @@ #include "content/renderer/shared_worker/embedded_shared_worker_stub.h" #include "gin/public/debug.h" #include "gpu/GLES2/gl2extchromium.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_platform_file.h" #include "ipc/mojo/ipc_channel_mojo.h" @@ -139,9 +136,9 @@ #include "mojo/common/common_type_converters.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "net/base/net_errors.h" -#include "net/base/net_util.h" #include "net/base/port_util.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/base/url_util.h" #include "skia/ext/event_tracer_impl.h" #include "skia/ext/skia_memory_dump_provider.h" #include "third_party/WebKit/public/platform/WebImageGenerator.h" @@ -171,6 +168,7 @@ #include "content/renderer/android/synchronous_compositor_filter.h" #include "content/renderer/media/android/renderer_demuxer_android.h" #include "content/renderer/media/android/stream_texture_factory_impl.h" +#include "media/base/android/media_codec_util.h" #endif #if defined(OS_MACOSX) @@ -186,13 +184,6 @@ #if defined(OS_WIN) #include #include -#else -// TODO(port) -#include "content/child/npapi/np_channel_base.h" -#endif - -#if defined(ENABLE_PLUGINS) -#include "content/renderer/npapi/plugin_channel_host.h" #endif #if defined(ENABLE_WEBRTC) @@ -363,16 +354,16 @@ void LowMemoryNotificationOnThisThread() { isolate->LowMemoryNotification(); } -class RenderFrameSetupImpl : public RenderFrameSetup { +class RenderFrameSetupImpl : public mojom::RenderFrameSetup { public: explicit RenderFrameSetupImpl( - mojo::InterfaceRequest request) + mojo::InterfaceRequest request) : routing_id_highmark_(-1), binding_(this, std::move(request)) {} - void ExchangeServiceProviders( + void ExchangeInterfaceProviders( int32_t frame_routing_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services) + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services) override { // TODO(morrita): This is for investigating http://crbug.com/415059 and // should be removed once it is fixed. @@ -395,26 +386,17 @@ class RenderFrameSetupImpl : public RenderFrameSetup { private: int32_t routing_id_highmark_; - mojo::StrongBinding binding_; + mojo::StrongBinding binding_; }; -void CreateRenderFrameSetup(mojo::InterfaceRequest request) { +void CreateRenderFrameSetup( + mojo::InterfaceRequest request) { new RenderFrameSetupImpl(std::move(request)); } -blink::WebGraphicsContext3D::Attributes GetOffscreenAttribs() { - blink::WebGraphicsContext3D::Attributes attributes; - attributes.shareResources = true; - attributes.depth = false; - attributes.stencil = false; - attributes.antialias = false; - attributes.noAutomaticFlushes = true; - return attributes; -} - void SetupEmbeddedWorkerOnWorkerThread( - mojo::InterfaceRequest services, - mojo::InterfacePtrInfo exposed_services) { + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtrInfo exposed_services) { ServiceWorkerContextClient* client = ServiceWorkerContextClient::ThreadSpecificInstance(); // It is possible for client to be null if for some reason the worker died @@ -426,16 +408,16 @@ void SetupEmbeddedWorkerOnWorkerThread( mojo::MakeProxy(std::move(exposed_services))); } -class EmbeddedWorkerSetupImpl : public EmbeddedWorkerSetup { +class EmbeddedWorkerSetupImpl : public mojom::EmbeddedWorkerSetup { public: explicit EmbeddedWorkerSetupImpl( - mojo::InterfaceRequest request) + mojo::InterfaceRequest request) : binding_(this, std::move(request)) {} - void ExchangeServiceProviders( + void ExchangeInterfaceProviders( int32_t thread_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services) override { + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services) override { WorkerThreadRegistry::Instance()->GetTaskRunnerFor(thread_id)->PostTask( FROM_HERE, base::Bind(&SetupEmbeddedWorkerOnWorkerThread, base::Passed(&services), @@ -443,11 +425,11 @@ class EmbeddedWorkerSetupImpl : public EmbeddedWorkerSetup { } private: - mojo::StrongBinding binding_; + mojo::StrongBinding binding_; }; void CreateEmbeddedWorkerSetup( - mojo::InterfaceRequest request) { + mojo::InterfaceRequest request) { new EmbeddedWorkerSetupImpl(std::move(request)); } @@ -537,6 +519,8 @@ std::string RenderThreadImpl::HistogramCustomizer::HostToCustomHistogramSuffix( return ".plus"; if (host == "inbox.google.com") return ".inbox"; + if (host == "calendar.google.com") + return ".calendar"; if (host == "www.youtube.com") return ".youtube"; if (IsAlexaTop10NonGoogleSite(host)) @@ -588,7 +572,9 @@ RenderThreadImpl* RenderThreadImpl::Create( const InProcessChildThreadParams& params) { scoped_ptr renderer_scheduler = scheduler::RendererScheduler::Create(); - return new RenderThreadImpl(params, std::move(renderer_scheduler)); + scoped_refptr test_task_counter; + return new RenderThreadImpl( + params, std::move(renderer_scheduler), test_task_counter); } // static @@ -605,14 +591,15 @@ RenderThreadImpl* RenderThreadImpl::current() { RenderThreadImpl::RenderThreadImpl( const InProcessChildThreadParams& params, - scoped_ptr scheduler) + scoped_ptr scheduler, + scoped_refptr& resource_task_queue) : ChildThreadImpl(Options::Builder() .InBrowserProcess(params) .UseMojoChannel(ShouldUseMojoChannel()) .Build()), renderer_scheduler_(std::move(scheduler)), raster_worker_pool_(new RasterWorkerPool()) { - Init(); + Init(resource_task_queue); } // When we run plugins in process, we actually run them on the render thread, @@ -626,10 +613,12 @@ RenderThreadImpl::RenderThreadImpl( renderer_scheduler_(std::move(scheduler)), main_message_loop_(std::move(main_message_loop)), raster_worker_pool_(new RasterWorkerPool()) { - Init(); + scoped_refptr test_task_counter; + Init(test_task_counter); } -void RenderThreadImpl::Init() { +void RenderThreadImpl::Init( + scoped_refptr& resource_task_queue) { TRACE_EVENT0("startup", "RenderThreadImpl::Init"); base::trace_event::TraceLog::GetInstance()->SetThreadSortIndex( @@ -646,6 +635,8 @@ void RenderThreadImpl::Init() { // Register this object as the main thread. ChildProcess::current()->set_main_thread(this); + InitializeWebKit(resource_task_queue); + // In single process the single process is all there is. notify_webkit_of_modal_loop_ = true; webkit_shared_timer_suspended_ = false; @@ -674,6 +665,8 @@ void RenderThreadImpl::Init() { media_stream_center_ = NULL; + blob_message_filter_ = new BlobMessageFilter(); + AddFilter(blob_message_filter_.get()); db_message_filter_ = new DBMessageFilter(); AddFilter(db_message_filter_.get()); @@ -732,10 +725,6 @@ void RenderThreadImpl::Init() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - cc::LayerSettings layer_settings; - layer_settings.use_compositor_animation_timelines = - !command_line.HasSwitch(switches::kDisableCompositorAnimationTimelines); - cc_blink::WebLayerImpl::SetLayerSettings(layer_settings); cc::SetClientNameForMetrics("Renderer"); is_threaded_animation_enabled_ = @@ -743,11 +732,11 @@ void RenderThreadImpl::Init() { is_zero_copy_enabled_ = command_line.HasSwitch(switches::kEnableZeroCopy); is_partial_raster_enabled_ = - command_line.HasSwitch(switches::kEnablePartialRaster); + !command_line.HasSwitch(switches::kDisablePartialRaster); is_gpu_memory_buffer_compositor_resources_enabled_ = command_line.HasSwitch( switches::kEnableGpuMemoryBufferCompositorResources); -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) is_elastic_overscroll_enabled_ = base::mac::IsOSLionOrLater(); if (is_elastic_overscroll_enabled_) { base::ScopedCFTypeRef key( @@ -806,6 +795,13 @@ void RenderThreadImpl::Init() { // been initialized by the Zygote before this instance became a Renderer. media::InitializeMediaLibrary(); +#if defined(OS_ANDROID) + if (!command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) && + media::MediaCodecUtil::IsMediaCodecAvailable()) { + media::EnablePlatformDecoderSupport(); + } +#endif + memory_pressure_listener_.reset(new base::MemoryPressureListener( base::Bind(&RenderThreadImpl::OnMemoryPressure, base::Unretained(this)))); @@ -817,20 +813,15 @@ void RenderThreadImpl::Init() { DCHECK(parsed_num_raster_threads) << string_value; DCHECK_GT(num_raster_threads, 0); +#if defined(OS_ANDROID) // Note: Currently, enabling image decode tasks only provides a benefit if - // there's more than one raster thread. This might change in the future but we - // avoid it for now to reduce the cost of recording. - are_image_decode_tasks_enabled_ = num_raster_threads > 1; - - base::SimpleThread::Options thread_options; -#if defined(OS_ANDROID) || defined(OS_LINUX) - if (!command_line.HasSwitch( - switches::kUseNormalPriorityForTileTaskWorkerThreads)) { - thread_options.set_priority(base::ThreadPriority::BACKGROUND); - } + // we use high quality interpolation filters, which are disabled on android. + are_image_decode_tasks_enabled_ = false; +#else + are_image_decode_tasks_enabled_ = true; #endif - raster_worker_pool_->Start(num_raster_threads, thread_options); + raster_worker_pool_->Start(num_raster_threads); // TODO(boliu): In single process, browser main loop should set up the // discardable memory manager, and should skip this if kSingleProcess. @@ -838,17 +829,20 @@ void RenderThreadImpl::Init() { base::DiscardableMemoryAllocator::SetInstance( ChildThreadImpl::discardable_shared_memory_manager()); - service_registry()->AddService( - base::Bind(CreateRenderFrameSetup)); - service_registry()->AddService( - base::Bind(CreateEmbeddedWorkerSetup)); + service_registry()->AddService(base::Bind(CreateRenderFrameSetup)); + service_registry()->AddService(base::Bind(CreateEmbeddedWorkerSetup)); #if defined(MOJO_SHELL_CLIENT) // We may not have a MojoShellConnection object in tests that directly // instantiate a RenderThreadImpl. - if (MojoShellConnection::Get()) + if (MojoShellConnection::Get() && + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUseMusInRenderer)) CreateRenderWidgetWindowTreeClientFactory(); #endif + + service_registry()->ConnectToRemoteService( + mojo::GetProxy(&storage_partition_service_)); } RenderThreadImpl::~RenderThreadImpl() { @@ -950,26 +944,33 @@ void RenderThreadImpl::Shutdown() { if (gpu_channel_.get()) gpu_channel_->DestroyChannel(); - // TODO(port) -#if defined(OS_WIN) - // Clean up plugin channels before this thread goes away. - NPChannelBase::CleanupChannels(); -#endif - ChildThreadImpl::Shutdown(); // Shut down the message loop and the renderer scheduler before shutting down // Blink. This prevents a scenario where a pending task in the message loop // accesses Blink objects after Blink shuts down. - // This must be at the very end of the shutdown sequence. You must not touch - // the message loop after this. renderer_scheduler_->Shutdown(); - main_message_loop_.reset(); + if (main_message_loop_) + main_message_loop_->RunUntilIdle(); + if (blink_platform_impl_) { blink_platform_impl_->Shutdown(); + // This must be at the very end of the shutdown sequence. + // blink::shutdown() must be called after all strong references from + // Chromium to Blink are cleared. blink::shutdown(); } + // Delay shutting down DiscardableSharedMemoryManager until blink::shutdown + // is complete, because blink::shutdown destructs Blink Resources and they + // may try to unlock their underlying discardable memory. + ChildThreadImpl::ShutdownDiscardableSharedMemoryManager(); + + // The message loop must be cleared after shutting down + // the DiscardableSharedMemoryManager, which needs to send messages + // to the browser process. + main_message_loop_.reset(); + lazy_tls.Pointer()->Set(NULL); } @@ -993,36 +994,16 @@ bool RenderThreadImpl::Send(IPC::Message* msg) { bool notify_webkit_of_modal_loop = true; // default value std::swap(notify_webkit_of_modal_loop, notify_webkit_of_modal_loop_); -#if defined(ENABLE_PLUGINS) - int render_view_id = MSG_ROUTING_NONE; -#endif - if (pumping_events) { renderer_scheduler_->SuspendTimerQueue(); if (notify_webkit_of_modal_loop) WebView::willEnterModalLoop(); -#if defined(ENABLE_PLUGINS) - RenderViewImpl* render_view = - RenderViewImpl::FromRoutingID(msg->routing_id()); - if (render_view) { - render_view_id = msg->routing_id(); - PluginChannelHost::Broadcast( - new PluginMsg_SignalModalDialogEvent(render_view_id)); - } -#endif } bool rv = ChildThreadImpl::Send(msg); if (pumping_events) { -#if defined(ENABLE_PLUGINS) - if (render_view_id != MSG_ROUTING_NONE) { - PluginChannelHost::Broadcast( - new PluginMsg_ResetModalDialogEvent(render_view_id)); - } -#endif - if (notify_webkit_of_modal_loop) WebView::didExitModalLoop(); @@ -1068,9 +1049,9 @@ void RenderThreadImpl::AddRoute(int32_t routing_id, IPC::Listener* listener) { return; scoped_refptr connection(it->second); - mojo::InterfaceRequest services( + mojo::shell::mojom::InterfaceProviderRequest services( std::move(connection->services())); - mojo::ServiceProviderPtr exposed_services( + mojo::shell::mojom::InterfaceProviderPtr exposed_services( std::move(connection->exposed_services())); exposed_services.set_connection_error_handler(mojo::Closure()); pending_render_frame_connects_.erase(it); @@ -1101,8 +1082,8 @@ void RenderThreadImpl::RemoveEmbeddedWorkerRoute(int32_t routing_id) { void RenderThreadImpl::RegisterPendingRenderFrameConnect( int routing_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services) { + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services) { std::pair result = pending_render_frame_connects_.insert(std::make_pair( routing_id, @@ -1111,6 +1092,10 @@ void RenderThreadImpl::RegisterPendingRenderFrameConnect( CHECK(result.second) << "Inserting a duplicate item."; } +mojom::StoragePartitionService* RenderThreadImpl::GetStoragePartitionService() { + return storage_partition_service_.get(); +} + int RenderThreadImpl::GenerateRoutingID() { int routing_id = MSG_ROUTING_NONE; Send(new ViewHostMsg_GenerateRoutingID(&routing_id)); @@ -1138,29 +1123,17 @@ void RenderThreadImpl::SetResourceDispatcherDelegate( resource_dispatcher()->set_delegate(delegate); } -void RenderThreadImpl::SetResourceDispatchTaskQueue( - const scoped_refptr& resource_task_queue) { - // Add a filter that forces resource messages to be dispatched via a - // particular task runner. - scoped_refptr filter( - new ResourceSchedulingFilter(resource_task_queue, resource_dispatcher())); - channel()->AddFilter(filter.get()); - resource_dispatcher()->SetResourceSchedulingFilter(filter); - - // The ChildResourceMessageFilter and the ResourceDispatcher need to use the - // same queue to ensure tasks are executed in the expected order. - child_resource_message_filter()->SetMainThreadTaskRunner(resource_task_queue); - resource_dispatcher()->SetMainThreadTaskRunner(resource_task_queue); -} - void RenderThreadImpl::InitializeCompositorThread() { #if defined(OS_ANDROID) SynchronousCompositorFactory* sync_compositor_factory = SynchronousCompositorFactory::GetInstance(); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); bool using_ipc_sync_compositing = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kIPCSyncCompositing); + cmd_line->HasSwitch(switches::kIPCSyncCompositing); + bool sync_input_for_sync_compositing = + cmd_line->HasSwitch(switches::kSyncInputForSyncCompositor); DCHECK(!sync_compositor_factory || !using_ipc_sync_compositing); + DCHECK(!sync_input_for_sync_compositing || using_ipc_sync_compositing); if (sync_compositor_factory) { compositor_task_runner_ = @@ -1174,23 +1147,30 @@ void RenderThreadImpl::InitializeCompositorThread() { #endif compositor_thread_.reset(new WebThreadForCompositor(options)); blink_platform_impl_->SetCompositorThread(compositor_thread_.get()); - compositor_task_runner_ = compositor_thread_->TaskRunner(); + compositor_task_runner_ = compositor_thread_->GetTaskRunner(); compositor_task_runner_->PostTask( FROM_HERE, base::Bind(base::IgnoreResult(&ThreadRestrictions::SetIOAllowed), false)); } - InputHandlerManagerClient* input_handler_manager_client = NULL; + InputHandlerManagerClient* input_handler_manager_client = nullptr; + SynchronousInputHandlerProxyClient* synchronous_input_handler_proxy_client = + nullptr; #if defined(OS_ANDROID) if (using_ipc_sync_compositing) { sync_compositor_message_filter_ = new SynchronousCompositorFilter(compositor_task_runner_); AddFilter(sync_compositor_message_filter_.get()); - input_handler_manager_client = sync_compositor_message_filter_.get(); + if (sync_input_for_sync_compositing) + input_handler_manager_client = sync_compositor_message_filter_.get(); + synchronous_input_handler_proxy_client = + sync_compositor_message_filter_.get(); } else if (sync_compositor_factory) { input_handler_manager_client = sync_compositor_factory->GetInputHandlerManagerClient(); + synchronous_input_handler_proxy_client = + sync_compositor_factory->GetSynchronousInputHandlerProxyClient(); } #endif if (!input_handler_manager_client) { @@ -1203,12 +1183,12 @@ void RenderThreadImpl::InitializeCompositorThread() { } input_handler_manager_.reset(new InputHandlerManager( compositor_task_runner_, input_handler_manager_client, - renderer_scheduler_.get())); + synchronous_input_handler_proxy_client, renderer_scheduler_.get())); } -void RenderThreadImpl::EnsureWebKitInitialized() { - if (blink_platform_impl_) - return; +void RenderThreadImpl::InitializeWebKit( + scoped_refptr& resource_task_queue) { + DCHECK(!blink_platform_impl_); const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); @@ -1220,8 +1200,9 @@ void RenderThreadImpl::EnsureWebKitInitialized() { SetRuntimeFeaturesDefaultsAndUpdateFromArgs(command_line); - blink_platform_impl_.reset( - new RendererBlinkPlatformImpl(renderer_scheduler_.get())); + blink_platform_impl_.reset(new RendererBlinkPlatformImpl( + renderer_scheduler_.get(), + static_cast(service_registry())->GetWeakPtr())); blink::initialize(blink_platform_impl_.get()); v8::Isolate* isolate = blink::mainThreadIsolate(); @@ -1235,7 +1216,26 @@ void RenderThreadImpl::EnsureWebKitInitialized() { base::Bind(base::IgnoreResult(&RenderThreadImpl::OnMessageReceived), base::Unretained(this))); - SetResourceDispatchTaskQueue(renderer_scheduler_->LoadingTaskRunner()); + scoped_refptr resource_task_queue2; + if (resource_task_queue) { + resource_task_queue2 = resource_task_queue; + } else { + resource_task_queue2 = renderer_scheduler_->LoadingTaskRunner(); + } + // Add a filter that forces resource messages to be dispatched via a + // particular task runner. + scoped_refptr filter( + new ResourceSchedulingFilter( + resource_task_queue2, resource_dispatcher())); + channel()->AddFilter(filter.get()); + resource_dispatcher()->SetResourceSchedulingFilter(filter); + + // The ChildResourceMessageFilter and the ResourceDispatcher need to use the + // same queue to ensure tasks are executed in the expected order. + child_resource_message_filter()->SetMainThreadTaskRunner( + resource_task_queue2); + resource_dispatcher()->SetMainThreadTaskRunner(resource_task_queue2); + if (!command_line.HasSwitch(switches::kDisableThreadedCompositing) && !command_line.HasSwitch(switches::kUseRemoteCompositing)) InitializeCompositorThread(); @@ -1267,8 +1267,6 @@ void RenderThreadImpl::EnsureWebKitInitialized() { RenderMediaClient::Initialize(); - FOR_EACH_OBSERVER(RenderProcessObserver, observers_, WebKitInitialized()); - devtools_agent_message_filter_ = new DevToolsAgentFilter(); AddFilter(devtools_agent_message_filter_.get()); @@ -1351,7 +1349,6 @@ cc::SharedBitmapManager* RenderThreadImpl::GetSharedBitmapManager() { } void RenderThreadImpl::RegisterExtension(v8::Extension* extension) { - EnsureWebKitInitialized(); WebScriptController::registerExtension(extension); } @@ -1462,40 +1459,30 @@ media::GpuVideoAcceleratorFactories* RenderThreadImpl::GetGpuFactories() { const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); -#if defined(OS_ANDROID) - if (SynchronousCompositorFactory::GetInstance()) { - if (!cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { - DLOG(WARNING) << "Accelerated video decoding is not explicitly disabled, " - "but is not supported by in-process rendering"; - } - return NULL; - } -#endif - scoped_refptr media_task_runner = GetMediaThreadTaskRunner(); scoped_refptr shared_context_provider = SharedWorkerContextProvider(); - scoped_refptr gpu_channel_host = GetGpuChannel(); + scoped_refptr gpu_channel_host = GetGpuChannel(); if (shared_context_provider && gpu_channel_host) { const bool enable_video_accelerator = !cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode); const bool enable_gpu_memory_buffer_video_frames = -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_LINUX) !cmd_line->HasSwitch(switches::kDisableGpuMemoryBufferVideoFrames); #else cmd_line->HasSwitch(switches::kEnableGpuMemoryBufferVideoFrames); #endif - std::string image_texture_target_string = + std::vector image_texture_targets; + std::string video_frame_image_texture_target_string = cmd_line->GetSwitchValueASCII(switches::kVideoImageTextureTarget); - unsigned image_texture_target = 0; - const bool parsed_image_texture_target = - base::StringToUint(image_texture_target_string, &image_texture_target); - DCHECK(parsed_image_texture_target); + StringToUintVector(video_frame_image_texture_target_string, + &image_texture_targets); + gpu_factories_.push_back(RendererGpuVideoAcceleratorFactories::Create( gpu_channel_host.get(), base::ThreadTaskRunnerHandle::Get(), media_task_runner, shared_context_provider, - enable_gpu_memory_buffer_video_frames, image_texture_target, + enable_gpu_memory_buffer_video_frames, image_texture_targets, enable_video_accelerator)); return gpu_factories_.back(); } @@ -1504,19 +1491,29 @@ media::GpuVideoAcceleratorFactories* RenderThreadImpl::GetGpuFactories() { scoped_ptr RenderThreadImpl::CreateOffscreenContext3d() { - blink::WebGraphicsContext3D::Attributes attributes(GetOffscreenAttribs()); - bool lose_context_when_out_of_memory = true; - - scoped_refptr gpu_channel_host(EstablishGpuChannelSync( + // This is used to create a few different offscreen contexts: + // - The shared main thread context (offscreen) used by blink for canvas + // - The worker context (offscreen) used for GPU raster and video decoding. + // This is for an offscreen context, so the default framebuffer doesn't need + // alpha, depth, stencil, antialiasing. + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 0; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.bind_generates_resource = false; + attributes.lose_context_when_out_of_memory = true; + bool share_resources = true; + bool automatic_flushes = false; + scoped_refptr gpu_channel_host(EstablishGpuChannelSync( CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE)); return make_scoped_ptr( WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( - gpu_channel_host.get(), - attributes, - lose_context_when_out_of_memory, + gpu_channel_host.get(), attributes, gfx::PreferIntegratedGpu, + share_resources, automatic_flushes, GURL("chrome://gpu/RenderThreadImpl::CreateOffscreenContext3d"), - WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), - NULL)); + WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(), NULL)); } scoped_refptr @@ -1535,16 +1532,30 @@ RenderThreadImpl::SharedMainThreadContextProvider() { } #if defined(OS_ANDROID) + +namespace { +base::LazyInstance> + g_stream_texture_factory_override; +} + +// static +void RenderThreadImpl::SetStreamTextureFactory( + scoped_refptr factory) { + g_stream_texture_factory_override.Get() = factory; +} + scoped_refptr RenderThreadImpl::GetStreamTexureFactory() { DCHECK(IsMainThread()); - if (!stream_texture_factory_.get() || - stream_texture_factory_->ContextGL()->GetGraphicsResetStatusKHR() != - GL_NO_ERROR) { + if (g_stream_texture_factory_override.Get()) { + stream_texture_factory_ = g_stream_texture_factory_override.Get(); + } else if (!stream_texture_factory_.get() || + stream_texture_factory_->ContextGL() + ->GetGraphicsResetStatusKHR() != GL_NO_ERROR) { if (!SharedMainThreadContextProvider().get()) { stream_texture_factory_ = NULL; return NULL; } - scoped_refptr gpu_channel_host(EstablishGpuChannelSync( + scoped_refptr gpu_channel_host(EstablishGpuChannelSync( CAUSE_FOR_GPU_LAUNCH_VIDEODECODEACCELERATOR_INITIALIZE)); if (!gpu_channel_host.get()) { LOG(ERROR) << "Failed to establish GPU channel for media player"; @@ -1556,6 +1567,12 @@ scoped_refptr RenderThreadImpl::GetStreamTexureFactory() { } return stream_texture_factory_; } + +bool RenderThreadImpl::EnableStreamTextureCopy() { + return !g_stream_texture_factory_override.Get() && + sync_compositor_message_filter_.get(); +} + #endif AudioRendererMixerManager* RenderThreadImpl::GetAudioRendererMixerManager() { @@ -1593,6 +1610,7 @@ void RenderThreadImpl::PreCacheFontCharacters(const LOGFONT& log_font, #endif // OS_WIN ServiceRegistry* RenderThreadImpl::GetServiceRegistry() { + DCHECK(service_registry()); return service_registry(); } @@ -1674,6 +1692,11 @@ RenderThreadImpl::CreateExternalBeginFrameSource(int routing_id) { compositor_message_filter_.get(), sync_message_filter(), routing_id)); } +cc::ImageSerializationProcessor* +RenderThreadImpl::GetImageSerializationProcessor() { + return GetContentClient()->renderer()->GetImageSerializationProcessor(); +} + cc::TaskGraphRunner* RenderThreadImpl::GetTaskGraphRunner() { return raster_worker_pool_->GetTaskGraphRunner(); } @@ -1700,14 +1723,6 @@ scoped_ptr RenderThreadImpl::AllocateSharedMemory( return HostAllocateSharedMemoryBuffer(size); } -CreateCommandBufferResult RenderThreadImpl::CreateViewCommandBuffer( - int32_t surface_id, - const GPUCreateCommandBufferConfig& init_params, - int32_t route_id) { - NOTREACHED(); - return CREATE_COMMAND_BUFFER_FAILED; -} - void RenderThreadImpl::DoNotNotifyWebKitOfModalLoop() { notify_webkit_of_modal_loop_ = false; } @@ -1780,6 +1795,15 @@ void RenderThreadImpl::OnCreateNewFrameProxy( int opener_routing_id, int parent_routing_id, const FrameReplicationState& replicated_state) { + // Debug cases of https://crbug.com/575245. + base::debug::SetCrashKeyValue("newproxy_proxy_id", + base::IntToString(routing_id)); + base::debug::SetCrashKeyValue("newproxy_view_id", + base::IntToString(render_view_routing_id)); + base::debug::SetCrashKeyValue("newproxy_opener_id", + base::IntToString(opener_routing_id)); + base::debug::SetCrashKeyValue("newproxy_parent_id", + base::IntToString(parent_routing_id)); RenderFrameProxy::CreateFrameProxy(routing_id, render_view_routing_id, opener_routing_id, parent_routing_id, replicated_state); @@ -1793,13 +1817,12 @@ void RenderThreadImpl::OnSetZoomLevelForCurrentURL(const std::string& scheme, } void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) { - EnsureWebKitInitialized(); CompositorDependencies* compositor_deps = this; // When bringing in render_view, also bring in webkit's glue and jsbindings. RenderViewImpl::Create(compositor_deps, params, false); } -GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync( +gpu::GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync( CauseForGpuLaunch cause_for_gpu_launch) { TRACE_EVENT0("gpu", "RenderThreadImpl::EstablishGpuChannelSync"); @@ -1818,10 +1841,8 @@ GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync( int client_id = 0; IPC::ChannelHandle channel_handle; gpu::GPUInfo gpu_info; - if (!Send(new GpuHostMsg_EstablishGpuChannel(cause_for_gpu_launch, - &client_id, - &channel_handle, - &gpu_info)) || + if (!Send(new ChildProcessHostMsg_EstablishGpuChannel( + cause_for_gpu_launch, &client_id, &channel_handle, &gpu_info)) || #if defined(OS_POSIX) channel_handle.socket.fd == -1 || #endif @@ -1836,13 +1857,9 @@ GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync( // implementation of GpuChannelHostFactory. io_thread_task_runner_ = ChildProcess::current()->io_task_runner(); - gpu_channel_ = - GpuChannelHost::Create(this, - client_id, - gpu_info, - channel_handle, - ChildProcess::current()->GetShutDownEvent(), - gpu_memory_buffer_manager()); + gpu_channel_ = gpu::GpuChannelHost::Create( + this, client_id, gpu_info, channel_handle, + ChildProcess::current()->GetShutDownEvent(), gpu_memory_buffer_manager()); return gpu_channel_.get(); } @@ -1869,7 +1886,7 @@ RenderThreadImpl::GetPeerConnectionDependencyFactory() { } #endif -GpuChannelHost* RenderThreadImpl::GetGpuChannel() { +gpu::GpuChannelHost* RenderThreadImpl::GetGpuChannel() { if (!gpu_channel_.get()) return NULL; @@ -1881,7 +1898,6 @@ GpuChannelHost* RenderThreadImpl::GetGpuChannel() { #if defined(ENABLE_PLUGINS) void RenderThreadImpl::OnPurgePluginListCache(bool reload_pages) { - EnsureWebKitInitialized(); // The call below will cause a GetPlugins call with refresh=true, but at this // point we already know that the browser has refreshed its list, so disable // refresh temporarily to prevent each renderer process causing the list to be @@ -1897,7 +1913,6 @@ void RenderThreadImpl::OnPurgePluginListCache(bool reload_pages) { void RenderThreadImpl::OnNetworkConnectionChanged( net::NetworkChangeNotifier::ConnectionType type, double max_bandwidth_mbps) { - EnsureWebKitInitialized(); bool online = type != net::NetworkChangeNotifier::CONNECTION_NONE; WebNetworkStateNotifier::setOnLine(online); FOR_EACH_OBSERVER( @@ -1920,7 +1935,6 @@ void RenderThreadImpl::OnUpdateTimezone(const std::string& zone_id) { #if defined(OS_ANDROID) void RenderThreadImpl::OnSetWebKitSharedTimersSuspended(bool suspend) { - EnsureWebKitInitialized(); if (suspend) { renderer_scheduler_->SuspendTimerQueue(); } else { @@ -1933,7 +1947,6 @@ void RenderThreadImpl::OnSetWebKitSharedTimersSuspended(bool suspend) { #if defined(OS_MACOSX) void RenderThreadImpl::OnUpdateScrollbarTheme( const ViewMsg_UpdateScrollbarTheme_Params& params) { - EnsureWebKitInitialized(); static_cast( blink_platform_impl_->scrollbarBehavior()) ->set_jump_on_track_click(params.jump_on_track_click); @@ -1941,7 +1954,7 @@ void RenderThreadImpl::OnUpdateScrollbarTheme( blink::WebScrollbarTheme::updateScrollbarsWithNSDefaults( params.initial_button_delay, params.autoscroll_button_delay, params.preferred_scroller_style, params.redraw, - params.scroll_animation_enabled, params.button_placement); + params.button_placement); } void RenderThreadImpl::OnSystemColorsChanged( @@ -1956,12 +1969,10 @@ void RenderThreadImpl::OnSystemColorsChanged( void RenderThreadImpl::OnCreateNewSharedWorker( const WorkerProcessMsg_CreateWorker_Params& params) { // EmbeddedSharedWorkerStub will self-destruct. - new EmbeddedSharedWorkerStub(params.url, - params.name, - params.content_security_policy, - params.security_policy_type, - params.pause_on_start, - params.route_id); + new EmbeddedSharedWorkerStub( + params.url, params.name, params.content_security_policy, + params.security_policy_type, params.creation_address_space, + params.pause_on_start, params.route_id); } void RenderThreadImpl::OnMemoryPressure( @@ -1977,10 +1988,13 @@ void RenderThreadImpl::OnMemoryPressure( // Trigger full v8 garbage collection on memory pressure notifications. // This will potentially hang the renderer for a long time, however, when // we receive a memory pressure notification, we might be about to be - // killed. - blink::mainThreadIsolate()->LowMemoryNotification(); - RenderThread::Get()->PostTaskToAllWebWorkers( - base::Bind(&LowMemoryNotificationOnThisThread)); + // killed. Because of the janky hang don't do this to foreground + // renderers. + if (RendererIsHidden()) { + blink::mainThreadIsolate()->LowMemoryNotification(); + RenderThread::Get()->PostTaskToAllWebWorkers( + base::Bind(&LowMemoryNotificationOnThisThread)); + } } if (memory_pressure_level == @@ -2088,19 +2102,19 @@ void RenderThreadImpl::WidgetRestored() { } void RenderThreadImpl::OnRendererHidden() { + blink::mainThreadIsolate()->IsolateInBackgroundNotification(); // TODO(rmcilroy): Remove IdleHandler and replace it with an IdleTask // scheduled by the RendererScheduler - http://crbug.com/469210. if (!GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) return; ScheduleIdleHandler(kInitialIdleHandlerDelayMs); - blink::mainThreadIsolate()->IsolateInBackgroundNotification(); } void RenderThreadImpl::OnRendererVisible() { + blink::mainThreadIsolate()->IsolateInForegroundNotification(); if (!GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) return; ScheduleIdleHandler(kLongIdleHandlerDelayMs); - blink::mainThreadIsolate()->IsolateInForegroundNotification(); } void RenderThreadImpl::ReleaseFreeMemory() { @@ -2113,14 +2127,15 @@ void RenderThreadImpl::ReleaseFreeMemory() { RenderThreadImpl::PendingRenderFrameConnect::PendingRenderFrameConnect( int routing_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services) + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services) : routing_id_(routing_id), services_(std::move(services)), exposed_services_(std::move(exposed_services)) { - // The RenderFrame may be deleted before the ExchangeServiceProviders message - // is received. In that case, the RenderFrameHost should close the connection, - // which is detected by setting an error handler on |exposed_services_|. + // The RenderFrame may be deleted before the ExchangeInterfaceProviders + // message is received. In that case, the RenderFrameHost should close the + // connection, which is detected by setting an error handler on + // |exposed_services_|. exposed_services_.set_connection_error_handler(base::Bind( &RenderThreadImpl::PendingRenderFrameConnect::OnConnectionError, base::Unretained(this))); diff --git a/chromium/content/renderer/render_thread_impl.h b/chromium/content/renderer/render_thread_impl.h index bc5b172624b..3d1b5bfc737 100644 --- a/chromium/content/renderer/render_thread_impl.h +++ b/chromium/content/renderer/render_thread_impl.h @@ -25,10 +25,11 @@ #include "content/child/child_thread_impl.h" #include "content/common/content_export.h" #include "content/common/frame_replication_state.h" -#include "content/common/gpu/client/gpu_channel_host.h" -#include "content/common/gpu/gpu_result_codes.h" +#include "content/common/gpu_process_launch_causes.h" +#include "content/common/storage_partition_service.mojom.h" #include "content/public/renderer/render_thread.h" #include "content/renderer/gpu/compositor_dependencies.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "net/base/network_change_notifier.h" #include "third_party/WebKit/public/platform/WebConnectionType.h" #include "ui/gfx/native_widget_types.h" @@ -58,6 +59,7 @@ class Thread; namespace cc { class ContextProvider; +class ImageSerializationProcessor; class TaskGraphRunner; } @@ -65,6 +67,10 @@ namespace cc_blink { class ContextProviderWebContext; } +namespace gpu { +class GpuChannelHost; +} + namespace IPC { class MessageFilter; } @@ -90,6 +96,7 @@ class AecDumpMessageFilter; class AudioInputMessageFilter; class AudioMessageFilter; class AudioRendererMixerManager; +class BlobMessageFilter; class BluetoothMessageFilter; class BrowserPluginManager; class CacheStorageDispatcher; @@ -99,7 +106,6 @@ class DBMessageFilter; class DevToolsAgentFilter; class DomStorageDispatcher; class EmbeddedWorkerDispatcher; -class GpuChannelHost; class IndexedDBDispatcher; class InputHandlerManager; class MediaStreamCenter; @@ -143,7 +149,7 @@ class SynchronousCompositorFilter; class CONTENT_EXPORT RenderThreadImpl : public RenderThread, public ChildThreadImpl, - public GpuChannelHostFactory, + public gpu::GpuChannelHostFactory, NON_EXPORTED_BASE(public CompositorDependencies) { public: static RenderThreadImpl* Create(const InProcessChildThreadParams& params); @@ -178,9 +184,6 @@ class CONTENT_EXPORT RenderThreadImpl void RemoveObserver(RenderProcessObserver* observer) override; void SetResourceDispatcherDelegate( ResourceDispatcherDelegate* delegate) override; - void EnsureWebKitInitialized() override; - void RecordAction(const base::UserMetricsAction& action) override; - void RecordComputedAction(const std::string& action) override; scoped_ptr HostAllocateSharedMemoryBuffer( size_t buffer_size) override; cc::SharedBitmapManager* GetSharedBitmapManager() override; @@ -216,6 +219,7 @@ class CONTENT_EXPORT RenderThreadImpl cc::ContextProvider* GetSharedMainThreadContextProvider() override; scoped_ptr CreateExternalBeginFrameSource( int routing_id) override; + cc::ImageSerializationProcessor* GetImageSerializationProcessor() override; cc::TaskGraphRunner* GetTaskGraphRunner() override; bool AreImageDecodeTasksEnabled() override; bool IsThreadedAnimationEnabled() override; @@ -224,8 +228,7 @@ class CONTENT_EXPORT RenderThreadImpl // established or if it has been lost (for example if the GPU plugin crashed). // If there is a pending asynchronous request, it will be completed by the // time this routine returns. - GpuChannelHost* EstablishGpuChannelSync(CauseForGpuLaunch); - + gpu::GpuChannelHost* EstablishGpuChannelSync(CauseForGpuLaunch); // This method modifies how the next message is sent. Normally, when sending // a synchronous message that runs a nested message loop, we need to suspend @@ -295,7 +298,11 @@ class CONTENT_EXPORT RenderThreadImpl return sync_compositor_message_filter_.get(); } + static void SetStreamTextureFactory( + scoped_refptr factory); + scoped_refptr GetStreamTexureFactory(); + bool EnableStreamTextureCopy(); #endif // Creates the embedder implementation of WebMediaStreamCenter. @@ -327,7 +334,7 @@ class CONTENT_EXPORT RenderThreadImpl // Get the GPU channel. Returns NULL if the channel is not established or // has been lost. - GpuChannelHost* GetGpuChannel(); + gpu::GpuChannelHost* GetGpuChannel(); // Returns a SingleThreadTaskRunner instance corresponding to the message loop // of the thread on which file operations should be run. Must be called @@ -447,35 +454,38 @@ class CONTENT_EXPORT RenderThreadImpl void RegisterPendingRenderFrameConnect( int routing_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services); + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services); + + mojom::StoragePartitionService* GetStoragePartitionService(); protected: - RenderThreadImpl(const InProcessChildThreadParams& params, - scoped_ptr scheduler); + RenderThreadImpl( + const InProcessChildThreadParams& params, + scoped_ptr scheduler, + scoped_refptr& resource_task_queue); RenderThreadImpl(scoped_ptr main_message_loop, scoped_ptr scheduler); - virtual void SetResourceDispatchTaskQueue( - const scoped_refptr& resource_task_queue); private: // ChildThread bool OnControlMessageReceived(const IPC::Message& msg) override; void OnProcessBackgrounded(bool backgrounded) override; + void RecordAction(const base::UserMetricsAction& action) override; + void RecordComputedAction(const std::string& action) override; // GpuChannelHostFactory implementation: bool IsMainThread() override; scoped_refptr GetIOThreadTaskRunner() override; scoped_ptr AllocateSharedMemory(size_t size) override; - CreateCommandBufferResult CreateViewCommandBuffer( - int32_t surface_id, - const GPUCreateCommandBufferConfig& init_params, - int32_t route_id) override; - void Init(); + void Init(scoped_refptr& resource_task_queue); void InitializeCompositorThread(); + void InitializeWebKit( + scoped_refptr& resource_task_queue); + void OnCreateNewFrame(FrameMsg_NewFrame_Params params); void OnCreateNewFrameProxy(int routing_id, int render_view_routing_id, @@ -531,6 +541,7 @@ class CONTENT_EXPORT RenderThreadImpl blink::WebMediaStreamCenter* media_stream_center_; // Used on the renderer and IPC threads. + scoped_refptr blob_message_filter_; scoped_refptr db_message_filter_; scoped_refptr audio_input_message_filter_; scoped_refptr audio_message_filter_; @@ -585,7 +596,7 @@ class CONTENT_EXPORT RenderThreadImpl base::RepeatingTimer idle_timer_; // The channel from the renderer process to the GPU process. - scoped_refptr gpu_channel_; + scoped_refptr gpu_channel_; // Cache of variables that are needed on the compositor thread by // GpuChannelHostFactory methods. @@ -664,6 +675,7 @@ class CONTENT_EXPORT RenderThreadImpl bool is_partial_raster_enabled_; bool is_elastic_overscroll_enabled_; std::vector use_image_texture_targets_; + std::vector use_video_frame_image_texture_targets_; bool are_image_decode_tasks_enabled_; bool is_threaded_animation_enabled_; @@ -672,14 +684,16 @@ class CONTENT_EXPORT RenderThreadImpl public: PendingRenderFrameConnect( int routing_id, - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services); + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services); - mojo::InterfaceRequest& services() { + mojo::shell::mojom::InterfaceProviderRequest& services() { return services_; } - mojo::ServiceProviderPtr& exposed_services() { return exposed_services_; } + mojo::shell::mojom::InterfaceProviderPtr& exposed_services() { + return exposed_services_; + } private: friend class base::RefCounted; @@ -690,14 +704,16 @@ class CONTENT_EXPORT RenderThreadImpl void OnConnectionError(); int routing_id_; - mojo::InterfaceRequest services_; - mojo::ServiceProviderPtr exposed_services_; + mojo::shell::mojom::InterfaceProviderRequest services_; + mojo::shell::mojom::InterfaceProviderPtr exposed_services_; }; typedef std::map> PendingRenderFrameConnectMap; PendingRenderFrameConnectMap pending_render_frame_connects_; + mojom::StoragePartitionServicePtr storage_partition_service_; + DISALLOW_COPY_AND_ASSIGN(RenderThreadImpl); }; diff --git a/chromium/content/renderer/render_thread_impl_browsertest.cc b/chromium/content/renderer/render_thread_impl_browsertest.cc index 95556d83cdf..f1b21a4f7c9 100644 --- a/chromium/content/renderer/render_thread_impl_browsertest.cc +++ b/chromium/content/renderer/render_thread_impl_browsertest.cc @@ -32,8 +32,13 @@ // IPC messages for testing ---------------------------------------------------- +// TODO(mdempsky): Fix properly by moving into a separate +// browsertest_message_generator.cc file. +#undef IPC_IPC_MESSAGE_MACROS_H_ +#undef IPC_MESSAGE_EXTRA #define IPC_MESSAGE_IMPL #include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_templates_impl.h" #undef IPC_MESSAGE_START #define IPC_MESSAGE_START TestMsgStart @@ -96,24 +101,18 @@ class TestTaskCounter : public base::SingleThreadTaskRunner { class RenderThreadImplForTest : public RenderThreadImpl { public: - RenderThreadImplForTest(const InProcessChildThreadParams& params, - scoped_ptr scheduler, - scoped_refptr test_task_counter) - : RenderThreadImpl(params, std::move(scheduler)), - test_task_counter_(test_task_counter) {} + RenderThreadImplForTest( + const InProcessChildThreadParams& params, + scoped_ptr scheduler, + scoped_refptr& test_task_counter) + : RenderThreadImpl(params, std::move(scheduler), test_task_counter) { + } ~RenderThreadImplForTest() override {} - void SetResourceDispatchTaskQueue( - const scoped_refptr&) override { - // Use our TestTaskCounter instead. - RenderThreadImpl::SetResourceDispatchTaskQueue(test_task_counter_); - } - using ChildThreadImpl::OnMessageReceived; private: - scoped_refptr test_task_counter_; }; #if defined(COMPILER_MSVC) @@ -180,14 +179,15 @@ class RenderThreadImplBrowserTest : public testing::Test { scoped_ptr renderer_scheduler = scheduler::RendererScheduler::Create(); InitializeMojo(); + scoped_refptr test_task_counter( + test_task_counter_.get()); thread_ = new RenderThreadImplForTest( InProcessChildThreadParams(test_helper_->GetChannelId(), - test_helper_->GetIOTaskRunner()), - std::move(renderer_scheduler), test_task_counter_); + test_helper_->GetIOTaskRunner(), + test_helper_->GetMessagePipeHandle()), + std::move(renderer_scheduler), test_task_counter); cmd->InitFromArgv(old_argv); - thread_->EnsureWebKitInitialized(); - test_msg_filter_ = make_scoped_refptr( new QuitOnTestMsgFilter(test_helper_->GetMessageLoop())); thread_->AddFilter(test_msg_filter_.get()); diff --git a/chromium/content/renderer/render_view_browsertest.cc b/chromium/content/renderer/render_view_browsertest.cc index 050d05fa8c8..c4c10b66c84 100644 --- a/chromium/content/renderer/render_view_browsertest.cc +++ b/chromium/content/renderer/render_view_browsertest.cc @@ -8,6 +8,8 @@ #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/location.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" @@ -15,11 +17,15 @@ #include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" #include "base/time/time.h" +#include "base/values.h" #include "base/win/windows_version.h" #include "build/build_config.h" +#include "cc/trees/layer_tree_host.h" #include "content/child/request_extra_data.h" #include "content/child/service_worker/service_worker_network_provider.h" +#include "content/common/content_switches_internal.h" #include "content/common/frame_messages.h" +#include "content/common/frame_replication_state.h" #include "content/common/site_isolation_policy.h" #include "content/common/ssl_status_serialization.h" #include "content/common/view_messages.h" @@ -41,6 +47,7 @@ #include "content/public/test/test_utils.h" #include "content/renderer/accessibility/renderer_accessibility.h" #include "content/renderer/devtools/devtools_agent.h" +#include "content/renderer/gpu/render_widget_compositor.h" #include "content/renderer/history_controller.h" #include "content/renderer/history_serialization.h" #include "content/renderer/navigation_state_impl.h" @@ -59,6 +66,7 @@ #include "third_party/WebKit/public/platform/WebURLResponse.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" +#include "third_party/WebKit/public/web/WebFrameContentDumper.h" #include "third_party/WebKit/public/web/WebHistoryCommitType.h" #include "third_party/WebKit/public/web/WebHistoryItem.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" @@ -87,6 +95,7 @@ #include "url/url_constants.h" using blink::WebFrame; +using blink::WebFrameContentDumper; using blink::WebInputEvent; using blink::WebLocalFrame; using blink::WebMouseEvent; @@ -152,6 +161,31 @@ bool TimeTicksGT(const base::TimeTicks& x, const base::TimeTicks& y) { return base::TimeTicks::IsHighResolution() ? x > y : x >= y; } +// FrameReplicationState is normally maintained in the browser process, +// but the function below provides a way for tests to construct a partial +// FrameReplicationState within the renderer process. We say "partial", +// because some fields of FrameReplicationState cannot be filled out +// by content-layer, renderer code (still the constructed, partial +// FrameReplicationState is sufficiently complete to avoid trigerring +// asserts that a default/empty FrameReplicationState would). +FrameReplicationState ReconstructReplicationStateForTesting( + TestRenderFrame* test_render_frame) { + blink::WebLocalFrame* frame = test_render_frame->GetWebFrame(); + + FrameReplicationState result; + // can't recover result.scope - no way to get WebTreeScopeType via public + // blink API... + result.name = base::UTF16ToUTF8(base::StringPiece16(frame->assignedName())); + result.unique_name = + base::UTF16ToUTF8(base::StringPiece16(frame->uniqueName())); + result.sandbox_flags = frame->effectiveSandboxFlags(); + // result.should_enforce_strict_mixed_content_checking is calculated in the + // browser... + result.origin = frame->getSecurityOrigin(); + + return result; +} + } // namespace class RenderViewImplTest : public RenderViewTest { @@ -345,11 +379,15 @@ class RenderViewImplTest : public RenderViewTest { class DevToolsAgentTest : public RenderViewImplTest { public: void Attach() { + notifications_ = std::vector(); std::string host_id = "host_id"; agent()->OnAttach(host_id, 17); + agent()->send_protocol_message_callback_for_test_ = base::Bind( + &DevToolsAgentTest::OnDevToolsMessage, base::Unretained(this)); } void Detach() { + agent()->send_protocol_message_callback_for_test_.Reset(); agent()->OnDetach(); } @@ -366,10 +404,38 @@ class DevToolsAgentTest : public RenderViewImplTest { view()->NotifyOnClose(); } + void OnDevToolsMessage( + int, int, const std::string& message, const std::string&) { + last_received_message_ = message; + std::unique_ptr root( + static_cast( + base::JSONReader::Read(message).release())); + int id; + if (!root->GetInteger("id", &id)) { + std::string notification; + EXPECT_TRUE(root->GetString("method", ¬ification)); + notifications_.push_back(notification); + } + } + + int CountNotifications(const std::string& notification) { + int result = 0; + for (const std::string& s : notifications_) { + if (s == notification) + ++result; + } + return result; + } + + std::string LastReceivedMessage() const { return last_received_message_; } + private: DevToolsAgent* agent() { return frame()->devtools_agent(); } + + std::vector notifications_; + std::string last_received_message_; }; class RenderViewImplBlinkSettingsTest : public RenderViewImplTest { @@ -393,9 +459,9 @@ class RenderViewImplBlinkSettingsTest : public RenderViewImplTest { }; class RenderViewImplScaleFactorTest : public RenderViewImplBlinkSettingsTest { - public: + protected: void SetDeviceScaleFactor(float dsf) { - ViewMsg_Resize_Params params; + ResizeParams params; params.screen_info.deviceScaleFactor = dsf; params.new_size = gfx::Size(100, 100); params.physical_backing_size = gfx::Size(200, 200); @@ -404,6 +470,33 @@ class RenderViewImplScaleFactorTest : public RenderViewImplBlinkSettingsTest { view()->OnResize(params); ASSERT_EQ(dsf, view()->device_scale_factor_); } + + void TestEmulatedSizeDprDsf(int width, int height, float dpr, + float compositor_dsf) { + static base::string16 get_width = + base::ASCIIToUTF16("Number(window.innerWidth)"); + static base::string16 get_height = + base::ASCIIToUTF16("Number(window.innerHeight)"); + static base::string16 get_dpr = + base::ASCIIToUTF16("Number(window.devicePixelRatio * 10)"); + + int emulated_width, emulated_height; + int emulated_dpr; + blink::WebDeviceEmulationParams params; + params.viewSize.width = width; + params.viewSize.height = height; + params.deviceScaleFactor = dpr; + view()->OnEnableDeviceEmulation(params); + EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_width, &emulated_width)); + EXPECT_EQ(width, emulated_width); + EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_height, + &emulated_height)); + EXPECT_EQ(height, emulated_height); + EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_dpr, &emulated_dpr)); + EXPECT_EQ(static_cast(dpr * 10), emulated_dpr); + EXPECT_EQ(compositor_dsf, + view()->compositor()->layer_tree_host()->device_scale_factor()); + } }; // Ensure that the main RenderFrame is deleted and cleared from the RenderView @@ -475,11 +568,7 @@ TEST_F(RenderViewImplTest, SaveImageFromDataURL) { // Test that we get form state change notifications when input fields change. TEST_F(RenderViewImplTest, OnNavStateChanged) { - // Don't want any delay for form state sync changes. This will still post a - // message so updates will get coalesced, but as soon as we spin the message - // loop, it will generate an update. view()->set_send_content_state_immediately(true); - LoadHTML(""); // We should NOT have gotten a form state change notification yet. @@ -494,6 +583,7 @@ TEST_F(RenderViewImplTest, OnNavStateChanged) { ExecuteJavaScriptForTests( "document.getElementById('elt_text').value = 'foo';"); ProcessPendingMessages(); + if (SiteIsolationPolicy::UseSubframeNavigationEntries()) { EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching( FrameHostMsg_UpdateState::ID)); @@ -501,7 +591,6 @@ TEST_F(RenderViewImplTest, OnNavStateChanged) { EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching( ViewHostMsg_UpdateState::ID)); } - ProcessPendingMessages(); } TEST_F(RenderViewImplTest, OnNavigationHttpPost) { @@ -512,6 +601,7 @@ TEST_F(RenderViewImplTest, OnNavigationHttpPost) { common_params.url = GURL("data:text/html,
Page
"); common_params.navigation_type = FrameMsg_Navigate_Type::NORMAL; common_params.transition = ui::PAGE_TRANSITION_TYPED; + common_params.method = "POST"; request_params.page_id = -1; // Set up post data. @@ -519,7 +609,6 @@ TEST_F(RenderViewImplTest, OnNavigationHttpPost) { "post \0\ndata"); const unsigned int length = 11; const std::vector post_data(raw_data, raw_data + length); - start_params.is_post = true; start_params.browser_initiated_post_data = post_data; frame()->Navigate(common_params, start_params, request_params); @@ -585,6 +674,11 @@ TEST_F(RenderViewImplTest, DecideNavigationPolicy) { // Navigations to normal HTTP URLs can be handled locally. blink::WebURLRequest request(GURL("http://foo.com")); + request.setFetchRequestMode(blink::WebURLRequest::FetchRequestModeNavigate); + request.setFetchCredentialsMode( + blink::WebURLRequest::FetchCredentialsModeInclude); + request.setFetchRedirectMode(blink::WebURLRequest::FetchRedirectModeManual); + request.setFrameType(blink::WebURLRequest::FrameTypeTopLevel); blink::WebFrameClient::NavigationPolicyInfo policy_info(request); policy_info.navigationType = blink::WebNavigationTypeLinkClicked; policy_info.defaultPolicy = blink::WebNavigationPolicyCurrentTab; @@ -595,7 +689,7 @@ TEST_F(RenderViewImplTest, DecideNavigationPolicy) { } else { // If this is a renderer-initiated navigation that just begun, it should // stop and be sent to the browser. - EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy); + EXPECT_EQ(blink::WebNavigationPolicyHandledByClient, policy); // If this a navigation that is ready to commit, it should be handled // locally. @@ -706,141 +800,6 @@ TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) { new_view->Release(); } -// Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is -// already swapped out. http://crbug.com/93427. -TEST_F(RenderViewImplTest, SendSwapOutACK) { - // This test is invalid in --site-per-process mode, as swapped-out is no - // longer used. - if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { - return; - } - LoadHTML("
Page A
"); - int initial_page_id = view_page_id(); - - // Increment the ref count so that we don't exit when swapping out. - RenderProcess::current()->AddRefProcess(); - - // Respond to a swap out request. - frame()->SwapOut(kProxyRoutingId, true, content::FrameReplicationState()); - - // Ensure the swap out commits synchronously. - EXPECT_NE(initial_page_id, view_page_id()); - - // Check for a valid OnSwapOutACK. - const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( - FrameHostMsg_SwapOut_ACK::ID); - ASSERT_TRUE(msg); - - // It is possible to get another swap out request. Ensure that we send - // an ACK, even if we don't have to do anything else. - render_thread_->sink().ClearMessages(); - frame()->SwapOut(kProxyRoutingId, false, content::FrameReplicationState()); - const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching( - FrameHostMsg_SwapOut_ACK::ID); - ASSERT_TRUE(msg2); - - // If we navigate back to this RenderView, ensure we don't send a state - // update for the swapped out URL. (http://crbug.com/72235) - CommonNavigationParams common_params; - RequestNavigationParams request_params; - common_params.url = GURL("data:text/html,
Page B
"); - common_params.navigation_type = FrameMsg_Navigate_Type::NORMAL; - common_params.transition = ui::PAGE_TRANSITION_TYPED; - request_params.current_history_list_length = 1; - request_params.current_history_list_offset = 0; - request_params.pending_history_list_offset = 1; - request_params.page_id = -1; - frame()->Navigate(common_params, StartNavigationParams(), request_params); - ProcessPendingMessages(); - const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching( - ViewHostMsg_UpdateState::ID); - EXPECT_FALSE(msg3); -} - -// Ensure the RenderViewImpl reloads the previous page if a reload request -// arrives while it is showing swappedout://. http://crbug.com/143155. -TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) { - // This test is invalid in --site-per-process mode, as swapped-out is no - // longer used. - if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) { - return; - } - - // Load page A. - LoadHTML("
Page A
"); - - // Load page B, which will trigger an UpdateState message for page A. - LoadHTML("
Page B
"); - - // Check for a valid UpdateState message for page A. - ProcessPendingMessages(); - const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching( - ViewHostMsg_UpdateState::ID); - ASSERT_TRUE(msg_A); - ViewHostMsg_UpdateState::Param params; - ViewHostMsg_UpdateState::Read(msg_A, ¶ms); - int page_id_A = base::get<0>(params); - PageState state_A = base::get<1>(params); - EXPECT_EQ(1, page_id_A); - render_thread_->sink().ClearMessages(); - - // Back to page A (page_id 1) and commit. - CommonNavigationParams common_params_A; - RequestNavigationParams request_params_A; - common_params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL; - common_params_A.transition = ui::PAGE_TRANSITION_FORWARD_BACK; - request_params_A.current_history_list_length = 2; - request_params_A.current_history_list_offset = 1; - request_params_A.pending_history_list_offset = 0; - request_params_A.page_id = 1; - request_params_A.nav_entry_id = 1; - request_params_A.page_state = state_A; - frame()->Navigate(common_params_A, StartNavigationParams(), request_params_A); - EXPECT_EQ(1, view()->historyBackListCount()); - EXPECT_EQ(2, view()->historyBackListCount() + - view()->historyForwardListCount() + 1); - ProcessPendingMessages(); - - // Respond to a swap out request. - frame()->SwapOut(kProxyRoutingId, true, content::FrameReplicationState()); - - // Check for a OnSwapOutACK. - const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( - FrameHostMsg_SwapOut_ACK::ID); - ASSERT_TRUE(msg); - render_thread_->sink().ClearMessages(); - - // It is possible to get a reload request at this point, containing the - // params.page_state of the initial page (e.g., if the new page fails the - // provisional load in the renderer process, after we unload the old page). - // Ensure the old page gets reloaded, not swappedout://. - CommonNavigationParams common_params; - RequestNavigationParams request_params; - common_params.url = GURL("data:text/html,
Page A
"); - common_params.navigation_type = FrameMsg_Navigate_Type::RELOAD; - common_params.transition = ui::PAGE_TRANSITION_RELOAD; - request_params.current_history_list_length = 2; - request_params.current_history_list_offset = 0; - request_params.pending_history_list_offset = 0; - request_params.page_id = 1; - request_params.nav_entry_id = 1; - request_params.page_state = state_A; - frame()->Navigate(common_params, StartNavigationParams(), request_params); - ProcessPendingMessages(); - - // Verify page A committed, not swappedout://. - const IPC::Message* frame_navigate_msg = - render_thread_->sink().GetUniqueMessageMatching( - FrameHostMsg_DidCommitProvisionalLoad::ID); - EXPECT_TRUE(frame_navigate_msg); - - // Read URL out of the parent trait of the params object. - FrameHostMsg_DidCommitProvisionalLoad::Param commit_load_params; - FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg, - &commit_load_params); - EXPECT_NE(GURL("swappedout://"), base::get<0>(commit_load_params).url); -} - // Verify that security origins are replicated properly to RenderFrameProxies // when swapping out. TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) { @@ -858,7 +817,8 @@ TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) { // Swap the child frame out and pass a replicated origin to be set for // WebRemoteFrame. - content::FrameReplicationState replication_state; + content::FrameReplicationState replication_state = + ReconstructReplicationStateForTesting(child_frame); replication_state.origin = url::Origin(GURL("http://foo.com")); child_frame->SwapOut(kProxyRoutingId, true, replication_state); @@ -866,7 +826,8 @@ TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) { EXPECT_TRUE(web_frame->firstChild()->isWebRemoteFrame()); // Expect the origin to be updated properly. - blink::WebSecurityOrigin origin = web_frame->firstChild()->securityOrigin(); + blink::WebSecurityOrigin origin = + web_frame->firstChild()->getSecurityOrigin(); EXPECT_EQ(origin.toString(), WebString::fromUTF8(replication_state.origin.Serialize())); @@ -877,7 +838,7 @@ TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) { RenderFrame::FromWebFrame(web_frame->lastChild())); child_frame2->SwapOut(kProxyRoutingId + 1, true, replication_state); EXPECT_TRUE(web_frame->lastChild()->isWebRemoteFrame()); - EXPECT_TRUE(web_frame->lastChild()->securityOrigin().isUnique()); + EXPECT_TRUE(web_frame->lastChild()->getSecurityOrigin().isUnique()); } // Test for https://crbug.com/568676, where a parent detaches a remote child @@ -896,7 +857,9 @@ TEST_F(RenderViewImplTest, NavigateProxyAndDetachBeforeOnNavigate) { RenderFrame::FromWebFrame(web_frame->firstChild())); // Swap the child frame out. - child_frame->SwapOut(kProxyRoutingId, true, content::FrameReplicationState()); + FrameReplicationState replication_state = + ReconstructReplicationStateForTesting(child_frame); + child_frame->SwapOut(kProxyRoutingId, true, replication_state); EXPECT_TRUE(web_frame->firstChild()->isWebRemoteFrame()); // Do the first step of a remote-to-local transition for the child proxy, @@ -907,13 +870,13 @@ TEST_F(RenderViewImplTest, NavigateProxyAndDetachBeforeOnNavigate) { widget_params.hidden = false; RenderFrameImpl::CreateFrame(routing_id, kProxyRoutingId, MSG_ROUTING_NONE, frame()->GetRoutingID(), MSG_ROUTING_NONE, - content::FrameReplicationState(), nullptr, - widget_params, blink::WebFrameOwnerProperties()); + replication_state, nullptr, widget_params, + blink::WebFrameOwnerProperties()); TestRenderFrame* provisional_frame = static_cast(RenderFrameImpl::FromRoutingID(routing_id)); EXPECT_TRUE(provisional_frame); - // Detach the child frame (current remote) in the main frame. + // Detach the child frame (currently remote) in the main frame. ExecuteJavaScriptForTests( "document.body.removeChild(document.querySelector('iframe'));"); RenderFrameProxy* child_proxy = @@ -935,6 +898,10 @@ TEST_F(RenderViewImplTest, NavigateProxyAndDetachBeforeOnNavigate) { render_thread_->sink().GetUniqueMessageMatching( FrameHostMsg_DidCommitProvisionalLoad::ID); EXPECT_FALSE(frame_navigate_msg); + + // Detach the provisional frame to clean it up. Normally, the browser + // process would trigger this via FrameMsg_Delete. + provisional_frame->GetWebFrame()->detach(); } // Verify that DidFlushPaint doesn't crash if called after a RenderView is @@ -951,8 +918,9 @@ TEST_F(RenderViewImplTest, PaintAfterSwapOut) { // Respond to a swap out request. TestRenderFrame* new_main_frame = static_cast(new_view->GetMainRenderFrame()); - new_main_frame->SwapOut(kProxyRoutingId, true, - content::FrameReplicationState()); + new_main_frame->SwapOut( + kProxyRoutingId, true, + ReconstructReplicationStateForTesting(new_main_frame)); // Simulate getting painted after swapping out. new_view->DidFlushPaint(); @@ -978,7 +946,8 @@ TEST_F(RenderViewImplTest, SetZoomLevelAfterCrossProcessNavigation) { // Swap the main frame out after which it should become a WebRemoteFrame. TestRenderFrame* main_frame = static_cast(view()->GetMainRenderFrame()); - main_frame->SwapOut(kProxyRoutingId, true, content::FrameReplicationState()); + main_frame->SwapOut(kProxyRoutingId, true, + ReconstructReplicationStateForTesting(main_frame)); EXPECT_TRUE(view()->webview()->mainFrame()->isWebRemoteFrame()); // This should not cause a crash. @@ -1098,7 +1067,6 @@ TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) { // changes. TEST_F(RenderViewImplTest, OnImeTypeChanged) { // Load an HTML page consisting of two input fields. - view()->set_send_content_state_immediately(true); LoadHTML("" "" "" @@ -1291,7 +1259,6 @@ TEST_F(RenderViewImplTest, ImeComposition) { // Load an HTML page consisting of a content-editable
element, // and move the input focus to the
element, where we can use // IMEs. - view()->set_send_content_state_immediately(true); LoadHTML("" "" "" @@ -1314,6 +1281,7 @@ TEST_F(RenderViewImplTest, ImeComposition) { view()->OnImeSetComposition( base::WideToUTF16(ime_message->ime_string), std::vector(), + gfx::Range::InvalidRange(), ime_message->selection_start, ime_message->selection_end); break; @@ -1329,6 +1297,7 @@ TEST_F(RenderViewImplTest, ImeComposition) { view()->OnImeSetComposition( base::string16(), std::vector(), + gfx::Range::InvalidRange(), 0, 0); break; } @@ -1343,8 +1312,8 @@ TEST_F(RenderViewImplTest, ImeComposition) { // Retrieve the content of this page and compare it with the expected // result. const int kMaxOutputCharacters = 128; - base::string16 output = - GetMainFrame()->contentAsText(kMaxOutputCharacters); + base::string16 output = WebFrameContentDumper::dumpWebViewAsText( + view()->GetWebView(), kMaxOutputCharacters); EXPECT_EQ(base::WideToUTF16(ime_message->result), output); } } @@ -1357,7 +1326,6 @@ TEST_F(RenderViewImplTest, OnSetTextDirection) { // This test changes the text direction of the "); + LoadHTML(""); ExecuteJavaScriptForTests("document.getElementById('test').focus();"); const base::string16 empty_string; @@ -1936,7 +1539,8 @@ TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { // ASCII composition const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo"); - view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0); + view()->OnImeSetComposition(ascii_composition, empty_underline, + gfx::Range::InvalidRange(), 0, 0); view()->GetCompositionCharacterBounds(&bounds); ASSERT_EQ(ascii_composition.size(), bounds.size()); @@ -1948,7 +1552,8 @@ TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { // Non surrogate pair unicode character. const base::string16 unicode_composition = base::UTF8ToUTF16( "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"); - view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0); + view()->OnImeSetComposition(unicode_composition, empty_underline, + gfx::Range::InvalidRange(), 0, 0); view()->GetCompositionCharacterBounds(&bounds); ASSERT_EQ(unicode_composition.size(), bounds.size()); for (size_t i = 0; i < bounds.size(); ++i) @@ -1961,6 +1566,7 @@ TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { base::UTF8ToUTF16("\xF0\xA0\xAE\x9F"); view()->OnImeSetComposition(surrogate_pair_char, empty_underline, + gfx::Range::InvalidRange(), 0, 0); view()->GetCompositionCharacterBounds(&bounds); @@ -1979,6 +1585,7 @@ TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { false, true, false, false, true, false, false, true }; view()->OnImeSetComposition(surrogate_pair_mixed_composition, empty_underline, + gfx::Range::InvalidRange(), 0, 0); view()->GetCompositionCharacterBounds(&bounds); @@ -2096,8 +1703,9 @@ TEST_F(RenderViewImplTest, NavigateSubframe) { // Copy the document content to std::wstring and compare with the // expected result. const int kMaxOutputCharacters = 256; - std::string output = base::UTF16ToUTF8(base::StringPiece16( - GetMainFrame()->contentAsText(kMaxOutputCharacters))); + std::string output = base::UTF16ToUTF8( + base::StringPiece16(WebFrameContentDumper::dumpWebViewAsText( + view()->GetWebView(), kMaxOutputCharacters))); EXPECT_EQ(output, "hello \n\nworld"); } @@ -2123,7 +1731,6 @@ TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) { } TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) { - view()->set_send_content_state_immediately(true); LoadHTML(""); view()->SetHandlingInputEventForTesting(true); @@ -2217,8 +1824,9 @@ TEST_F(RendererErrorPageTest, MAYBE_Suppresses) { main_frame->didFailProvisionalLoad(web_frame, error, blink::WebStandardCommit); const int kMaxOutputCharacters = 22; - EXPECT_EQ("", base::UTF16ToASCII( - base::StringPiece16(web_frame->contentAsText(kMaxOutputCharacters)))); + EXPECT_EQ("", base::UTF16ToASCII(base::StringPiece16( + WebFrameContentDumper::dumpWebViewAsText( + view()->GetWebView(), kMaxOutputCharacters)))); } #if defined(OS_ANDROID) @@ -2252,8 +1860,9 @@ TEST_F(RendererErrorPageTest, MAYBE_DoesNotSuppress) { FrameLoadWaiter(main_frame).Wait(); const int kMaxOutputCharacters = 22; EXPECT_EQ("A suffusion of yellow.", - base::UTF16ToASCII(base::StringPiece16( - web_frame->contentAsText(kMaxOutputCharacters)))); + base::UTF16ToASCII( + base::StringPiece16(WebFrameContentDumper::dumpWebViewAsText( + view()->GetWebView(), kMaxOutputCharacters)))); } #if defined(OS_ANDROID) @@ -2279,15 +1888,17 @@ TEST_F(RendererErrorPageTest, MAYBE_HttpStatusCodeErrorWithEmptyBody) { RequestNavigationParams()); // Emulate a 4xx/5xx main resource response with an empty body. - main_frame->didReceiveResponse(web_frame, 1, response); - main_frame->didFinishDocumentLoad(web_frame, true); + main_frame->didReceiveResponse(1, response); + main_frame->didFinishDocumentLoad(web_frame); + main_frame->runScriptsAtDocumentReady(web_frame, true); // The error page itself is loaded asynchronously. FrameLoadWaiter(main_frame).Wait(); const int kMaxOutputCharacters = 22; EXPECT_EQ("A suffusion of yellow.", - base::UTF16ToASCII(base::StringPiece16( - web_frame->contentAsText(kMaxOutputCharacters)))); + base::UTF16ToASCII( + base::StringPiece16(WebFrameContentDumper::dumpWebViewAsText( + view()->GetWebView(), kMaxOutputCharacters)))); } // Ensure the render view sends favicon url update events correctly. @@ -2355,7 +1966,7 @@ TEST_F(RenderViewImplTest, ServiceWorkerNetworkProviderSetup) { DocumentState::FromDataSource(GetMainFrame()->dataSource())); ASSERT_TRUE(provider); extra_data = static_cast( - GetMainFrame()->dataSource()->request().extraData()); + GetMainFrame()->dataSource()->request().getExtraData()); ASSERT_TRUE(extra_data); EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id()); @@ -2368,7 +1979,7 @@ TEST_F(RenderViewImplTest, ServiceWorkerNetworkProviderSetup) { ASSERT_TRUE(provider); EXPECT_NE(provider1_id, provider->provider_id()); extra_data = static_cast( - GetMainFrame()->dataSource()->request().extraData()); + GetMainFrame()->dataSource()->request().getExtraData()); ASSERT_TRUE(extra_data); EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id()); @@ -2379,7 +1990,7 @@ TEST_F(RenderViewImplTest, ServiceWorkerNetworkProviderSetup) { request.setRequestContext(blink::WebURLRequest::RequestContextSubresource); blink::WebURLResponse redirect_response; frame()->willSendRequest(GetMainFrame(), 0, request, redirect_response); - extra_data = static_cast(request.extraData()); + extra_data = static_cast(request.getExtraData()); ASSERT_TRUE(extra_data); EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id()); @@ -2402,36 +2013,6 @@ TEST_F(RenderViewImplTest, OnSetAccessibilityMode) { ASSERT_NE((RendererAccessibility*) NULL, frame()->renderer_accessibility()); } -TEST_F(RenderViewImplTest, ScreenMetricsEmulation) { - LoadHTML(""); - - blink::WebDeviceEmulationParams params; - base::string16 get_width = base::ASCIIToUTF16("Number(window.innerWidth)"); - base::string16 get_height = base::ASCIIToUTF16("Number(window.innerHeight)"); - int width, height; - - params.viewSize.width = 327; - params.viewSize.height = 415; - view()->OnEnableDeviceEmulation(params); - EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_width, &width)); - EXPECT_EQ(params.viewSize.width, width); - EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_height, &height)); - EXPECT_EQ(params.viewSize.height, height); - - params.viewSize.width = 1005; - params.viewSize.height = 1102; - view()->OnEnableDeviceEmulation(params); - EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_width, &width)); - EXPECT_EQ(params.viewSize.width, width); - EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(get_height, &height)); - EXPECT_EQ(params.viewSize.height, height); - - view()->OnDisableDeviceEmulation(); - - view()->OnEnableDeviceEmulation(params); - // Don't disable here to test that emulation is being shutdown properly. -} - // Sanity check for the Navigation Timing API |navigationStart| override. We // are asserting only most basic constraints, as TimeTicks (passed as the // override) are not comparable with the wall time (returned by the Blink API). @@ -2440,15 +2021,14 @@ TEST_F(RenderViewImplTest, NavigationStartOverride) { // days from now is *not* reported as one that starts in the future; as we // sanitize the override allowing a maximum of ::Now(). CommonNavigationParams late_common_params; - StartNavigationParams late_start_params; late_common_params.url = GURL("data:text/html,
Another page
"); late_common_params.navigation_type = FrameMsg_Navigate_Type::NORMAL; late_common_params.transition = ui::PAGE_TRANSITION_TYPED; late_common_params.navigation_start = base::TimeTicks::Now() + base::TimeDelta::FromDays(42); - late_start_params.is_post = true; + late_common_params.method = "POST"; - frame()->Navigate(late_common_params, late_start_params, + frame()->Navigate(late_common_params, StartNavigationParams(), RequestNavigationParams()); ProcessPendingMessages(); base::Time after_navigation = @@ -2507,25 +2087,30 @@ TEST_F(RenderViewImplTest, BrowserNavigationStartNotUsedForHistoryNavigation) { ProcessPendingMessages(); render_thread_->sink().ClearMessages(); - CommonNavigationParams common_params; - common_params.transition = ui::PAGE_TRANSITION_FORWARD_BACK; // Go back. - GoToOffsetWithParams(-1, back_state, common_params, StartNavigationParams(), - RequestNavigationParams()); + CommonNavigationParams common_params_back; + common_params_back.url = + GURL("data:text/html;charset=utf-8,
Page B
"); + common_params_back.transition = ui::PAGE_TRANSITION_FORWARD_BACK; + GoToOffsetWithParams(-1, back_state, common_params_back, + StartNavigationParams(), RequestNavigationParams()); FrameHostMsg_DidStartProvisionalLoad::Param host_nav_params = ProcessAndReadIPC(); EXPECT_PRED2(TimeTicksGT, base::get<1>(host_nav_params), - common_params.navigation_start); + common_params_back.navigation_start); render_thread_->sink().ClearMessages(); // Go forward. - GoToOffsetWithParams(1, forward_state, common_params, - StartNavigationParams(), - RequestNavigationParams()); + CommonNavigationParams common_params_forward; + common_params_forward.url = + GURL("data:text/html;charset=utf-8,
Page C
"); + common_params_forward.transition = ui::PAGE_TRANSITION_FORWARD_BACK; + GoToOffsetWithParams(1, forward_state, common_params_forward, + StartNavigationParams(), RequestNavigationParams()); FrameHostMsg_DidStartProvisionalLoad::Param host_nav_params2 = ProcessAndReadIPC(); EXPECT_PRED2(TimeTicksGT, base::get<1>(host_nav_params2), - common_params.navigation_start); + common_params_forward.navigation_start); } TEST_F(RenderViewImplTest, BrowserNavigationStartSuccessfullyTransmitted) { @@ -2603,6 +2188,8 @@ TEST_F(RenderViewImplBlinkSettingsTest, Negative) { TEST_F(RenderViewImplScaleFactorTest, ConverViewportToWindowWithoutZoomForDSF) { DoSetUp(); + if (IsUseZoomForDSFEnabled()) + return; SetDeviceScaleFactor(2.f); blink::WebRect rect(20, 10, 200, 100); view()->convertViewportToWindow(&rect); @@ -2612,6 +2199,66 @@ TEST_F(RenderViewImplScaleFactorTest, ConverViewportToWindowWithoutZoomForDSF) { EXPECT_EQ(100, rect.height); } +TEST_F(RenderViewImplScaleFactorTest, ScreenMetricsEmulationWithOriginalDSF1) { + DoSetUp(); + SetDeviceScaleFactor(1.f); + + LoadHTML(""); + { + SCOPED_TRACE("327x415 1dpr"); + TestEmulatedSizeDprDsf(327, 415, 1.f, 1.f); + } + { + SCOPED_TRACE("327x415 1.5dpr"); + TestEmulatedSizeDprDsf(327, 415, 1.5f, 1.f); + } + { + SCOPED_TRACE("1005x1102 2dpr"); + TestEmulatedSizeDprDsf(1005, 1102, 2.f, 1.f); + } + { + SCOPED_TRACE("1005x1102 3dpr"); + TestEmulatedSizeDprDsf(1005, 1102, 3.f, 1.f); + } + + view()->OnDisableDeviceEmulation(); + + blink::WebDeviceEmulationParams params; + view()->OnEnableDeviceEmulation(params); + // Don't disable here to test that emulation is being shutdown properly. +} + +TEST_F(RenderViewImplScaleFactorTest, ScreenMetricsEmulationWithOriginalDSF2) { + DoSetUp(); + SetDeviceScaleFactor(2.f); + float compositor_dsf = + IsUseZoomForDSFEnabled() ? 1.f : 2.f; + + LoadHTML(""); + { + SCOPED_TRACE("327x415 1dpr"); + TestEmulatedSizeDprDsf(327, 415, 1.f, compositor_dsf); + } + { + SCOPED_TRACE("327x415 1.5dpr"); + TestEmulatedSizeDprDsf(327, 415, 1.5f, compositor_dsf); + } + { + SCOPED_TRACE("1005x1102 2dpr"); + TestEmulatedSizeDprDsf(1005, 1102, 2.f, compositor_dsf); + } + { + SCOPED_TRACE("1005x1102 3dpr"); + TestEmulatedSizeDprDsf(1005, 1102, 3.f, compositor_dsf); + } + + view()->OnDisableDeviceEmulation(); + + blink::WebDeviceEmulationParams params; + view()->OnEnableDeviceEmulation(params); + // Don't disable here to test that emulation is being shutdown properly. +} + TEST_F(RenderViewImplScaleFactorTest, ConverViewportToWindowWithZoomForDSF) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableUseZoomForDSF); @@ -2638,7 +2285,8 @@ TEST_F(RenderViewImplScaleFactorTest, ConverViewportToWindowWithZoomForDSF) { } #if defined(OS_MACOSX) || defined(USE_AURA) -TEST_F(RenderViewImplScaleFactorTest, GetCompositionCharacterBoundsTest) { +TEST_F(RenderViewImplScaleFactorTest, + DISABLED_GetCompositionCharacterBoundsTest) { // http://crbug.com/582016 base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableUseZoomForDSF); DoSetUp(); @@ -2659,7 +2307,8 @@ TEST_F(RenderViewImplScaleFactorTest, GetCompositionCharacterBoundsTest) { // ASCII composition const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo"); - view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0); + view()->OnImeSetComposition(ascii_composition, empty_underline, + gfx::Range::InvalidRange(), 0, 0); view()->GetCompositionCharacterBounds(&bounds_at_1x); ASSERT_EQ(ascii_composition.size(), bounds_at_1x.size()); @@ -2699,12 +2348,12 @@ TEST_F(RenderViewImplScaleFactorTest, AutoResizeWithZoomForDSF) { DoSetUp(); view()->EnableAutoResizeForTesting(gfx::Size(5, 5), gfx::Size(1000, 1000)); LoadHTML(kAutoResizeTestPage); - gfx::Size size_at_1x = view()->size(); + gfx::Size size_at_1x = view()->GetWidget()->size(); ASSERT_FALSE(size_at_1x.IsEmpty()); SetDeviceScaleFactor(2.f); LoadHTML(kAutoResizeTestPage); - gfx::Size size_at_2x = view()->size(); + gfx::Size size_at_2x = view()->GetWidget()->size(); EXPECT_EQ(size_at_1x, size_at_2x); } @@ -2712,12 +2361,12 @@ TEST_F(RenderViewImplScaleFactorTest, AutoResizeWithoutZoomForDSF) { DoSetUp(); view()->EnableAutoResizeForTesting(gfx::Size(5, 5), gfx::Size(1000, 1000)); LoadHTML(kAutoResizeTestPage); - gfx::Size size_at_1x = view()->size(); + gfx::Size size_at_1x = view()->GetWidget()->size(); ASSERT_FALSE(size_at_1x.IsEmpty()); SetDeviceScaleFactor(2.f); LoadHTML(kAutoResizeTestPage); - gfx::Size size_at_2x = view()->size(); + gfx::Size size_at_2x = view()->GetWidget()->size(); EXPECT_EQ(size_at_1x, size_at_2x); } @@ -2740,4 +2389,66 @@ TEST_F(DevToolsAgentTest, DevToolsResumeOnClose) { Detach(); } +TEST_F(DevToolsAgentTest, RuntimeEnableForcesContexts) { + LoadHTML("page"); + Attach(); + DispatchDevToolsMessage("{\"id\":1,\"method\":\"Runtime.enable\"}"); + EXPECT_EQ(2, CountNotifications("Runtime.executionContextCreated")); +} + +TEST_F(DevToolsAgentTest, RuntimeEnableForcesContextsAfterNavigation) { + Attach(); + DispatchDevToolsMessage("{\"id\":1,\"method\":\"Runtime.enable\"}"); + EXPECT_EQ(0, CountNotifications("Runtime.executionContextCreated")); + LoadHTML("page"); + EXPECT_EQ(2, CountNotifications("Runtime.executionContextCreated")); +} + +TEST_F(DevToolsAgentTest, RuntimeEvaluateRunMicrotasks) { + LoadHTML("page"); + Attach(); + DispatchDevToolsMessage("{\"id\":1,\"method\":\"Console.enable\"}"); + DispatchDevToolsMessage("{\"id\":2," + "\"method\":\"Runtime.evaluate\"," + "\"params\":{" + "\"expression\":\"Promise.resolve().then(" + "() => console.log(42));\"" + "}" + "}"); + EXPECT_EQ(1, CountNotifications("Console.messageAdded")); +} + +TEST_F(DevToolsAgentTest, RuntimeCallFunctionOnRunMicrotasks) { + LoadHTML("page"); + Attach(); + DispatchDevToolsMessage("{\"id\":1,\"method\":\"Console.enable\"}"); + DispatchDevToolsMessage("{\"id\":2," + "\"method\":\"Runtime.evaluate\"," + "\"params\":{" + "\"expression\":\"window\"" + "}" + "}"); + + std::unique_ptr root( + static_cast( + base::JSONReader::Read(LastReceivedMessage()).release())); + const base::Value* object_id; + ASSERT_TRUE(root->Get("result.result.objectId", &object_id)); + std::string object_id_str; + EXPECT_TRUE(base::JSONWriter::Write(*object_id, &object_id_str)); + + DispatchDevToolsMessage("{\"id\":3," + "\"method\":\"Runtime.callFunctionOn\"," + "\"params\":{" + "\"objectId\":" + + object_id_str + + "," + "\"functionDeclaration\":\"function foo(){ " + "Promise.resolve().then(() => " + "console.log(239))}\"" + "}" + "}"); + EXPECT_EQ(1, CountNotifications("Console.messageAdded")); +} + } // namespace content diff --git a/chromium/content/renderer/render_view_browsertest_mac.mm b/chromium/content/renderer/render_view_browsertest_mac.mm index a973c313c4e..4e1b7320832 100644 --- a/chromium/content/renderer/render_view_browsertest_mac.mm +++ b/chromium/content/renderer/render_view_browsertest_mac.mm @@ -10,11 +10,15 @@ #include "content/public/test/render_view_test.h" #include "content/renderer/render_view_impl.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebFrameContentDumper.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "third_party/WebKit/public/web/WebView.h" #include // for the kVK_* constants. #include +using blink::WebFrameContentDumper; + namespace content { NSEvent* CmdDeadKeyEvent(NSEventType type, unsigned short code) { @@ -104,7 +108,8 @@ TEST_F(RenderViewTest, MacTestCmdUp) { SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); - output = GetMainFrame()->contentAsText(kMaxOutputCharacters); + output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), + kMaxOutputCharacters); EXPECT_EQ(kArrowDownScrollDown, base::UTF16ToASCII(output)); const char* kArrowUpScrollUp = "38,false,false,true,false\n0\np1"; @@ -113,7 +118,8 @@ TEST_F(RenderViewTest, MacTestCmdUp) { SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); - output = GetMainFrame()->contentAsText(kMaxOutputCharacters); + output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), + kMaxOutputCharacters); EXPECT_EQ(kArrowUpScrollUp, base::UTF16ToASCII(output)); // Now let javascript eat the key events -- no scrolling should happen. @@ -127,7 +133,8 @@ TEST_F(RenderViewTest, MacTestCmdUp) { SendNativeKeyEvent(NativeWebKeyboardEvent(arrowDownKeyDown)); ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); - output = GetMainFrame()->contentAsText(kMaxOutputCharacters); + output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), + kMaxOutputCharacters); EXPECT_EQ(kArrowDownNoScroll, base::UTF16ToASCII(output)); const char* kArrowUpNoScroll = "38,false,false,true,false\n100\np1"; @@ -136,7 +143,8 @@ TEST_F(RenderViewTest, MacTestCmdUp) { SendNativeKeyEvent(NativeWebKeyboardEvent(arrowUpKeyDown)); ProcessPendingMessages(); ExecuteJavaScriptForTests("scroll.textContent = window.pageYOffset"); - output = GetMainFrame()->contentAsText(kMaxOutputCharacters); + output = WebFrameContentDumper::dumpWebViewAsText(view->GetWebView(), + kMaxOutputCharacters); EXPECT_EQ(kArrowUpNoScroll, base::UTF16ToASCII(output)); } diff --git a/chromium/content/renderer/render_view_impl.cc b/chromium/content/renderer/render_view_impl.cc index c2c266c3162..4da799e6cf1 100644 --- a/chromium/content/renderer/render_view_impl.cc +++ b/chromium/content/renderer/render_view_impl.cc @@ -13,6 +13,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/debug/alias.h" +#include "base/debug/crash_logging.h" #include "base/files/file_path.h" #include "base/i18n/rtl.h" #include "base/json/json_writer.h" @@ -31,10 +32,10 @@ #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "cc/base/switches.h" #include "content/child/appcache/appcache_dispatcher.h" #include "content/child/appcache/web_application_cache_host_impl.h" #include "content/child/child_shared_bitmap_manager.h" -#include "content/child/npapi/webplugin_delegate_impl.h" #include "content/child/request_extra_data.h" #include "content/child/v8_value_converter_impl.h" #include "content/child/webmessageportchannel_impl.h" @@ -46,7 +47,9 @@ #include "content/common/frame_messages.h" #include "content/common/frame_replication_state.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/common/input/input_event_utils.h" #include "content/common/input_messages.h" +#include "content/common/page_messages.h" #include "content/common/pepper_messages.h" #include "content/common/site_isolation_policy.h" #include "content/common/ssl_status_serialization.h" @@ -109,7 +112,8 @@ #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/http/http_util.h" #include "skia/ext/platform_canvas.h" -#include "third_party/WebKit/public/platform/WebCString.h" +#include "third_party/WebKit/public/platform/FilePathConversion.h" +#include "third_party/WebKit/public/platform/URLConversion.h" #include "third_party/WebKit/public/platform/WebConnectionType.h" #include "third_party/WebKit/public/platform/WebDragData.h" #include "third_party/WebKit/public/platform/WebHTTPBody.h" @@ -135,10 +139,11 @@ #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebFileChooserParams.h" -#include "third_party/WebKit/public/web/WebFindOptions.h" #include "third_party/WebKit/public/web/WebFormControlElement.h" #include "third_party/WebKit/public/web/WebFormElement.h" #include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebFrameContentDumper.h" +#include "third_party/WebKit/public/web/WebFrameWidget.h" #include "third_party/WebKit/public/web/WebHistoryItem.h" #include "third_party/WebKit/public/web/WebHitTestResult.h" #include "third_party/WebKit/public/web/WebInputElement.h" @@ -149,13 +154,10 @@ #include "third_party/WebKit/public/web/WebPageImportanceSignals.h" #include "third_party/WebKit/public/web/WebPlugin.h" #include "third_party/WebKit/public/web/WebPluginAction.h" -#include "third_party/WebKit/public/web/WebPluginContainer.h" -#include "third_party/WebKit/public/web/WebPluginDocument.h" #include "third_party/WebKit/public/web/WebRange.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSearchableFormData.h" -#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebSettings.h" #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" @@ -172,6 +174,7 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/native_widget_types.h" +#include "url/url_constants.h" #include "v8/include/v8.h" #if defined(OS_ANDROID) @@ -181,8 +184,6 @@ #include "content/renderer/android/content_detector.h" #include "content/renderer/android/email_detector.h" #include "content/renderer/android/phone_number_detector.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebFloatRect.h" #include "ui/gfx/geometry/rect_f.h" #elif defined(OS_WIN) @@ -196,7 +197,6 @@ #endif #if defined(ENABLE_PLUGINS) -#include "content/renderer/npapi/webplugin_delegate_proxy.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/pepper/pepper_plugin_registry.h" #endif @@ -209,7 +209,6 @@ using blink::WebAXObject; using blink::WebApplicationCacheHost; using blink::WebApplicationCacheHostClient; -using blink::WebCString; using blink::WebColor; using blink::WebConsoleMessage; using blink::WebData; @@ -220,10 +219,10 @@ using blink::WebDragOperation; using blink::WebDragOperationsMask; using blink::WebElement; using blink::WebFileChooserCompletion; -using blink::WebFindOptions; using blink::WebFormControlElement; using blink::WebFormElement; using blink::WebFrame; +using blink::WebFrameContentDumper; using blink::WebGestureEvent; using blink::WebHistoryItem; using blink::WebHTTPBody; @@ -242,8 +241,6 @@ using blink::WebPeerConnection00HandlerClient; using blink::WebPeerConnectionHandler; using blink::WebPeerConnectionHandlerClient; using blink::WebPluginAction; -using blink::WebPluginContainer; -using blink::WebPluginDocument; using blink::WebPoint; using blink::WebRange; using blink::WebRect; @@ -276,8 +273,6 @@ using base::TimeDelta; #if defined(OS_ANDROID) using blink::WebContentDetectionResult; -using blink::WebFloatPoint; -using blink::WebFloatRect; using blink::WebHitTestResult; #endif @@ -316,8 +311,10 @@ static RenderViewImpl* (*g_create_render_view_impl)( Referrer RenderViewImpl::GetReferrerFromRequest( WebFrame* frame, const WebURLRequest& request) { - return Referrer(GURL(request.httpHeaderField(WebString::fromUTF8("Referer"))), - request.referrerPolicy()); + return Referrer( + blink::WebStringToGURL(request.httpHeaderField( + WebString::fromUTF8("Referer"))), + request.referrerPolicy()); } // static @@ -619,8 +616,6 @@ RenderViewImpl::RenderViewImpl(CompositorDependencies* compositor_deps, send_preferred_size_changes_(false), navigation_gesture_(NavigationGestureUnknown), opened_by_user_gesture_(true), - opener_suppressed_(false), - suppress_dialogs_until_swap_out_(false), page_id_(-1), next_page_id_(params.next_page_id), history_list_offset_(-1), @@ -640,16 +635,13 @@ RenderViewImpl::RenderViewImpl(CompositorDependencies* compositor_deps, #if defined(OS_ANDROID) expected_content_intent_id_(0), #endif -#if defined(OS_WIN) - focused_plugin_id_(-1), -#endif #if defined(ENABLE_PLUGINS) - plugin_find_handler_(NULL), focused_pepper_plugin_(NULL), pepper_last_mouse_event_target_(NULL), #endif enumeration_completion_id_(0), session_storage_namespace_id_(params.session_storage_namespace_id) { + GetWidget()->set_owner_delegate(this); } void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, @@ -671,7 +663,7 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, webwidget_mouse_lock_target_.reset(new WebWidgetLockTarget(webwidget_)); g_view_map.Get().insert(std::make_pair(webview(), this)); - g_routing_id_view_map.Get().insert(std::make_pair(routing_id(), this)); + g_routing_id_view_map.Get().insert(std::make_pair(GetRoutingID(), this)); const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); @@ -679,28 +671,51 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, if (command_line.HasSwitch(switches::kStatsCollectionController)) stats_collection_observer_.reset(new StatsCollectionObserver(this)); + // Debug cases of https://crbug.com/575245. + base::debug::SetCrashKeyValue("rvinit_view_id", + base::IntToString(GetRoutingID())); + base::debug::SetCrashKeyValue("rvinit_proxy_id", + base::IntToString(params.proxy_routing_id)); + base::debug::SetCrashKeyValue( + "rvinit_main_frame_id", base::IntToString(params.main_frame_routing_id)); + + webview()->setDisplayMode(display_mode_); + webview()->settings()->setPreferCompositingToLCDTextEnabled( + PreferCompositingToLCDText(compositor_deps_, device_scale_factor_)); + webview()->settings()->setThreadedScrollingEnabled( + !command_line.HasSwitch(switches::kDisableThreadedScrolling)); + webview()->settings()->setRootLayerScrolls( + command_line.HasSwitch(switches::kRootLayerScrolls)); + webview()->setShowFPSCounter( + command_line.HasSwitch(cc::switches::kShowFPSCounter)); + + ApplyWebPreferencesInternal(webkit_preferences_, webview(), compositor_deps_); + + if (switches::IsTouchDragDropEnabled()) + webview()->settings()->setTouchDragDropEnabled(true); + + WebSettings::SelectionStrategyType selection_strategy = + WebSettings::SelectionStrategyType::Character; + const std::string selection_strategy_str = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kTouchTextSelectionStrategy); + if (selection_strategy_str == "direction") + selection_strategy = WebSettings::SelectionStrategyType::Direction; + webview()->settings()->setSelectionStrategy(selection_strategy); + + ApplyBlinkSettings(command_line, webview()->settings()); + if (params.main_frame_routing_id != MSG_ROUTING_NONE) { main_render_frame_ = RenderFrameImpl::CreateMainFrame( this, params.main_frame_routing_id, params.main_frame_widget_routing_id, - params.hidden, screen_info(), compositor_deps_); + params.hidden, screen_info(), compositor_deps_, opener_frame); } if (params.proxy_routing_id != MSG_ROUTING_NONE) { CHECK(params.swapped_out); - if (main_render_frame_) { - DCHECK(!SiteIsolationPolicy::IsSwappedOutStateForbidden()); - RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyToReplaceFrame( - main_render_frame_, params.proxy_routing_id, - blink::WebTreeScopeType::Document); - main_render_frame_->set_render_frame_proxy(proxy); - } else { - DCHECK(SiteIsolationPolicy::IsSwappedOutStateForbidden()); - // Pass MSG_ROUTING_NONE for opener, since actual opener (if any) will be - // set separately below. - RenderFrameProxy::CreateFrameProxy(params.proxy_routing_id, routing_id(), - MSG_ROUTING_NONE, MSG_ROUTING_NONE, - params.replicated_frame_state); - } + RenderFrameProxy::CreateFrameProxy( + params.proxy_routing_id, GetRoutingID(), params.opener_frame_route_id, + MSG_ROUTING_NONE, params.replicated_frame_state); } if (main_render_frame_) @@ -717,7 +732,7 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, content_detectors_.push_back(make_scoped_ptr(new EmailDetector())); #endif - RenderThread::Get()->AddRoute(routing_id(), this); + RenderThread::Get()->AddRoute(GetRoutingID(), this); // Take a reference on behalf of the RenderThread. This will be balanced // when we receive ViewMsg_Close in the RenderWidget (which RenderView // inherits from). @@ -732,28 +747,6 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, // completing initialization. Otherwise, we can finish it now. if (opener_id_ == MSG_ROUTING_NONE) did_show_ = true; - UpdateWebViewWithDeviceScaleFactor(); - webview()->setDisplayMode(display_mode_); - webview()->settings()->setPreferCompositingToLCDTextEnabled( - PreferCompositingToLCDText(compositor_deps_, device_scale_factor_)); - webview()->settings()->setThreadedScrollingEnabled( - !command_line.HasSwitch(switches::kDisableThreadedScrolling)); - webview()->settings()->setRootLayerScrolls( - command_line.HasSwitch(switches::kRootLayerScrolls)); - - ApplyWebPreferencesInternal(webkit_preferences_, webview(), compositor_deps_); - - if (switches::IsTouchDragDropEnabled()) - webview()->settings()->setTouchDragDropEnabled(true); - - WebSettings::SelectionStrategyType selection_strategy = - WebSettings::SelectionStrategyType::Character; - const std::string selection_strategy_str = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kTouchTextSelectionStrategy); - if (selection_strategy_str == "direction") - selection_strategy = WebSettings::SelectionStrategyType::Direction; - webview()->settings()->setSelectionStrategy(selection_strategy); // Set the main frame's name. Only needs to be done for WebLocalFrames, // since the remote case was handled as part of SetReplicatedState on the @@ -768,10 +761,9 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, if (params.window_was_created_with_opener) webview()->setOpenedByDOM(); + UpdateWebViewWithDeviceScaleFactor(); OnSetRendererPrefs(params.renderer_preferences); - ApplyBlinkSettings(command_line, webview()->settings()); - if (!params.enable_auto_resize) { OnResize(params.initial_size); } else { @@ -799,15 +791,13 @@ void RenderViewImpl::Initialize(const ViewMsg_New_Params& params, GetContentClient()->renderer()->RenderViewCreated(this); - // If we have an opener_frame but we weren't created by a renderer, then it's - // the browser asking us to set our opener to another frame. - if (opener_frame && !was_created_by_renderer) - webview()->mainFrame()->setOpener(opener_frame); - - // If we are initially swapped out, navigate to kSwappedOutURL. - // This ensures we are in a unique origin that others cannot script. - if (is_swapped_out_ && webview()->mainFrame()->isWebLocalFrame()) - main_render_frame_->NavigateToSwappedOutURL(); + // Ensure that sandbox flags are inherited from an opener in a different + // process. In that case, the browser process will set any inherited sandbox + // flags in |replicated_frame_state|, so apply them here. + if (!was_created_by_renderer && webview()->mainFrame()->isWebLocalFrame()) { + webview()->mainFrame()->toWebLocalFrame()->forceSandboxFlags( + params.replicated_frame_state.sandbox_flags); + } } RenderViewImpl::~RenderViewImpl() { @@ -946,16 +936,10 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setAllowFileAccessFromFileURLs( prefs.allow_file_access_from_file_urls); - // Enable the web audio API if requested on the command line. - settings->setWebAudioEnabled(prefs.webaudio_enabled); - // Enable experimental WebGL support if requested on command line // and support is compiled in. settings->setExperimentalWebGLEnabled(prefs.experimental_webgl_enabled); - // Disable GL multisampling if requested on command line. - settings->setOpenGLMultisamplingEnabled(prefs.gl_multisampling_enabled); - // Enable WebGL errors to the JS console if requested. settings->setWebGLErrorsToConsoleEnabled( prefs.webgl_errors_to_console_enabled); @@ -1006,6 +990,8 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setStrictPowerfulFeatureRestrictions( prefs.strict_powerful_feature_restrictions); + settings->setAllowGeolocationOnInsecureOrigins( + prefs.allow_geolocation_on_insecure_origins); settings->setPasswordEchoEnabled(prefs.password_echo_enabled); settings->setShouldPrintBackgrounds(prefs.should_print_backgrounds); settings->setShouldClearDocumentBackground( @@ -1031,7 +1017,6 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setShouldRespectImageOrientation( prefs.should_respect_image_orientation); - settings->setUnsafePluginPastingEnabled(false); settings->setEditingBehavior( static_cast(prefs.editing_behavior)); @@ -1039,6 +1024,8 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setInertVisualViewport(prefs.inert_visual_viewport); + settings->setMainFrameClipsContent(!prefs.record_whole_document); + settings->setSmartInsertDeleteEnabled(prefs.smart_insert_delete_enabled); settings->setSpatialNavigationEnabled(prefs.spatial_navigation_enabled); @@ -1051,6 +1038,11 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setImageAnimationPolicy( static_cast(prefs.animation_policy)); + settings->setPresentationRequiresUserGesture( + prefs.user_gesture_required_for_presentation); + + settings->setTextTrackMarginPercentage(prefs.text_track_margin_percentage); + // Needs to happen before setIgnoreVIewportTagScaleLimits below. web_view->setDefaultPageScaleLimits( prefs.default_minimum_page_scale_factor, @@ -1091,8 +1083,9 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, prefs.ignore_main_frame_overflow_hidden_quirk); settings->setReportScreenSizeInPhysicalPixelsQuirk( prefs.report_screen_size_in_physical_pixels_quirk); + settings->setShouldReuseGlobalForUnownedMainFrame( + prefs.resue_global_for_unowned_main_frame); settings->setPreferHiddenVolumeControls(true); - settings->setMainFrameClipsContent(!prefs.record_whole_document); settings->setShrinksViewportContentToFit(true); settings->setUseMobileViewportStyle(true); settings->setAutoplayExperimentMode( @@ -1115,6 +1108,8 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setDoubleTapToZoomEnabled(true); web_view->setMaximumLegibleScale(prefs.default_maximum_page_scale_factor); #endif + + settings->setWheelGesturesEnabled(UseGestureBasedWheelScrolling()); } /*static*/ @@ -1189,61 +1184,29 @@ void RenderViewImpl::PepperFocusChanged(PepperPluginInstanceImpl* instance, UpdateSelectionBounds(); } -void RenderViewImpl::RegisterPluginDelegate(WebPluginDelegateProxy* delegate) { - plugin_delegates_.insert(delegate); - // If the renderer is visible, set initial visibility and focus state. - if (!is_hidden()) { -#if defined(OS_MACOSX) - delegate->SetContainerVisibility(true); - if (webview() && webview()->isActive()) - delegate->SetWindowFocus(true); -#endif - } - // Plugins start assuming the content has focus (so that they work in - // environments where RenderView isn't hosting them), so we always have to - // set the initial state. See webplugin_delegate_impl.h for details. - delegate->SetContentAreaFocus(has_focus()); -} - -void RenderViewImpl::UnregisterPluginDelegate( - WebPluginDelegateProxy* delegate) { - plugin_delegates_.erase(delegate); -} - -#if defined(OS_WIN) -void RenderViewImpl::PluginFocusChanged(bool focused, int plugin_id) { - if (focused) - focused_plugin_id_ = plugin_id; - else - focused_plugin_id_ = -1; -} -#endif - #if defined(OS_MACOSX) -void RenderViewImpl::PluginFocusChanged(bool focused, int plugin_id) { - Send(new ViewHostMsg_PluginFocusChanged(routing_id(), focused, plugin_id)); -} - void RenderViewImpl::OnGetRenderedText() { if (!webview()) return; + + if (!webview()->mainFrame()->isWebLocalFrame()) + return; + // Get rendered text from WebLocalFrame. // TODO: Currently IPC truncates any data that has a // size > kMaximumMessageSize. May be split the text into smaller chunks and // send back using multiple IPC. See http://crbug.com/393444. static const size_t kMaximumMessageSize = 8 * 1024 * 1024; - std::string text = webview()->mainFrame()->contentAsText( - kMaximumMessageSize).utf8(); - - Send(new ViewMsg_GetRenderedTextCompleted(routing_id(), text)); -} + // TODO(dglazkov): Using this API is wrong. It's not OOPIF-compatible and + // sends text in the wrong order. See http://crbug.com/584798. + // TODO(dglazkov): WebFrameContentDumper should only be used for + // testing purposes. See http://crbug.com/585164. + std::string text = + WebFrameContentDumper::deprecatedDumpFrameTreeAsText( + webview()->mainFrame()->toWebLocalFrame(), kMaximumMessageSize) + .utf8(); -void RenderViewImpl::StartPluginIme() { - IPC::Message* msg = new ViewHostMsg_StartPluginIme(routing_id()); - // This message can be sent during event-handling, and needs to be delivered - // within that context. - msg->set_unblock(true); - Send(msg); + Send(new ViewMsg_GetRenderedTextCompleted(GetRoutingID(), text)); } #endif // defined(OS_MACOSX) @@ -1257,22 +1220,22 @@ void RenderViewImpl::TransferActiveWheelFlingAnimation( // RenderWidgetInputHandlerDelegate ----------------------------------------- -void RenderViewImpl::FocusChangeComplete() { - RenderWidget::FocusChangeComplete(); +void RenderViewImpl::RenderWidgetFocusChangeComplete() { FOR_EACH_OBSERVER(RenderViewObserver, observers_, FocusChangeComplete()); } -bool RenderViewImpl::HasTouchEventHandlersAt(const gfx::Point& point) const { +bool RenderViewImpl::DoesRenderWidgetHaveTouchEventHandlersAt( + const gfx::Point& point) const { if (!webview()) return false; return webview()->hasTouchEventHandlersAt(point); } -void RenderViewImpl::OnDidHandleKeyEvent() { +void RenderViewImpl::RenderWidgetDidHandleKeyEvent() { ClearEditCommands(); } -bool RenderViewImpl::WillHandleGestureEvent( +bool RenderViewImpl::RenderWidgetWillHandleGestureEvent( const blink::WebGestureEvent& event) { possible_drag_event_info_.event_source = ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH; @@ -1281,7 +1244,8 @@ bool RenderViewImpl::WillHandleGestureEvent( return false; } -bool RenderViewImpl::WillHandleMouseEvent(const blink::WebMouseEvent& event) { +bool RenderViewImpl::RenderWidgetWillHandleMouseEvent( + const blink::WebMouseEvent& event) { possible_drag_event_info_.event_source = ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE; possible_drag_event_info_.event_location = @@ -1331,8 +1295,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { OnSetEditCommandsForNextKeyEvent) IPC_MESSAGE_HANDLER(ViewMsg_CopyImageAt, OnCopyImageAt) IPC_MESSAGE_HANDLER(ViewMsg_SaveImageAt, OnSaveImageAt) - IPC_MESSAGE_HANDLER(ViewMsg_Find, OnFind) - IPC_MESSAGE_HANDLER(ViewMsg_StopFinding, OnStopFinding) IPC_MESSAGE_HANDLER(ViewMsg_SetPageScale, OnSetPageScale) IPC_MESSAGE_HANDLER(ViewMsg_Zoom, OnZoom) IPC_MESSAGE_HANDLER(ViewMsg_SetZoomLevelForLoadingURL, @@ -1356,8 +1318,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_EnumerateDirectoryResponse, OnEnumerateDirectoryResponse) IPC_MESSAGE_HANDLER(ViewMsg_RunFileChooserResponse, OnFileChooserResponse) - IPC_MESSAGE_HANDLER(ViewMsg_SuppressDialogsUntilSwapOut, - OnSuppressDialogsUntilSwapOut) IPC_MESSAGE_HANDLER(ViewMsg_ClosePage, OnClosePage) IPC_MESSAGE_HANDLER(ViewMsg_ThemeChanged, OnThemeChanged) IPC_MESSAGE_HANDLER(ViewMsg_MoveOrResizeStarted, OnMoveOrResizeStarted) @@ -1377,27 +1337,20 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { // TODO(viettrungluu): Move to a separate message filter. IPC_MESSAGE_HANDLER(ViewMsg_SetHistoryOffsetAndLength, OnSetHistoryOffsetAndLength) - IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode) IPC_MESSAGE_HANDLER(ViewMsg_ReleaseDisambiguationPopupBitmap, OnReleaseDisambiguationPopupBitmap) IPC_MESSAGE_HANDLER(ViewMsg_ForceRedraw, OnForceRedraw) IPC_MESSAGE_HANDLER(ViewMsg_SelectWordAroundCaret, OnSelectWordAroundCaret) + IPC_MESSAGE_HANDLER(PageMsg_UpdateWindowScreenRect, + OnUpdateWindowScreenRect) #if defined(OS_ANDROID) - IPC_MESSAGE_HANDLER(InputMsg_ActivateNearestFindResult, - OnActivateNearestFindResult) - IPC_MESSAGE_HANDLER(ViewMsg_FindMatchRects, OnFindMatchRects) IPC_MESSAGE_HANDLER(ViewMsg_UpdateTopControlsState, OnUpdateTopControlsState) IPC_MESSAGE_HANDLER(ViewMsg_ExtractSmartClipData, OnExtractSmartClipData) #elif defined(OS_MACOSX) IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedText, OnGetRenderedText) - IPC_MESSAGE_HANDLER(ViewMsg_PluginImeCompositionCompleted, - OnPluginImeCompositionCompleted) IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose) - IPC_MESSAGE_HANDLER(ViewMsg_SetInLiveResize, OnSetInLiveResize) - IPC_MESSAGE_HANDLER(ViewMsg_SetWindowVisibility, OnSetWindowVisibility) - IPC_MESSAGE_HANDLER(ViewMsg_WindowFrameChanged, OnWindowFrameChanged) #endif // Adding a new message? Add platform independent ones first, then put the // platform specific ones at the end. @@ -1429,7 +1382,7 @@ void RenderViewImpl::OnSaveImageAt(int x, int y) { void RenderViewImpl::OnUpdateTargetURLAck() { // Check if there is a targeturl waiting to be sent. if (target_url_status_ == TARGET_PENDING) - Send(new ViewHostMsg_UpdateTargetURL(routing_id(), pending_target_url_)); + Send(new ViewHostMsg_UpdateTargetURL(GetRoutingID(), pending_target_url_)); target_url_status_ = TARGET_NONE; } @@ -1447,16 +1400,16 @@ void RenderViewImpl::OnMoveCaret(const gfx::Point& point) { if (!webview()) return; - Send(new InputHostMsg_MoveCaret_ACK(routing_id())); - - webview()->focusedFrame()->moveCaretSelection(point); + Send(new InputHostMsg_MoveCaret_ACK(GetRoutingID())); + webview()->focusedFrame()->moveCaretSelection( + ConvertWindowPointToViewport(point)); } void RenderViewImpl::OnScrollFocusedEditableNodeIntoRect( const gfx::Rect& rect) { if (has_scrolled_focused_editable_node_into_rect_ && rect == rect_for_scrolled_focused_editable_node_) { - FocusChangeComplete(); + GetWidget()->FocusChangeComplete(); return; } @@ -1469,7 +1422,7 @@ void RenderViewImpl::OnScrollFocusedEditableNodeIntoRect( } if (!will_animate) - FocusChangeComplete(); + GetWidget()->FocusChangeComplete(); } void RenderViewImpl::OnSetEditCommandsForNextKeyEvent( @@ -1492,16 +1445,9 @@ void RenderViewImpl::OnSetInitialFocus(bool reverse) { webview()->setInitialFocus(reverse); } -#if defined(OS_MACOSX) -void RenderViewImpl::OnSetInLiveResize(bool in_live_resize) { - if (!webview()) - return; - if (in_live_resize) - webview()->willStartLiveResize(); - else - webview()->willEndLiveResize(); +void RenderViewImpl::OnUpdateWindowScreenRect(gfx::Rect window_screen_rect) { + RenderWidget::OnUpdateWindowScreenRect(window_screen_rect); } -#endif /////////////////////////////////////////////////////////////////////////////// @@ -1513,11 +1459,7 @@ void RenderViewImpl::SendUpdateState() { if (!entry) return; - // Don't send state updates for kSwappedOutURL. - if (entry->root().urlString() == kSwappedOutURL) - return; - - Send(new ViewHostMsg_UpdateState(routing_id(), page_id_, + Send(new ViewHostMsg_UpdateState(GetRoutingID(), page_id_, HistoryEntryToPageState(entry))); } @@ -1540,24 +1482,6 @@ void RenderViewImpl::ApplyWebPreferencesInternal( blink::WebView* web_view, CompositorDependencies* compositor_deps) { ApplyWebPreferences(prefs, web_view); -#if defined(OS_MACOSX) && !defined(OS_IOS) - DCHECK(compositor_deps); - bool is_elastic_overscroll_enabled = - compositor_deps->IsElasticOverscrollEnabled(); - web_view->settings()->setReportWheelOverscroll(is_elastic_overscroll_enabled); -#endif -} - -bool RenderViewImpl::SendAndRunNestedMessageLoop(IPC::SyncMessage* message) { - // Before WebKit asks us to show an alert (etc.), it takes care of doing the - // equivalent of WebView::willEnterModalLoop. In the case of showModalDialog - // it is particularly important that we do not call willEnterModalLoop as - // that would defer resource loads for the dialog itself. - if (RenderThreadImpl::current()) // Will be NULL during unit tests. - RenderThreadImpl::current()->DoNotNotifyWebKitOfModalLoop(); - - message->EnableMessagePumping(); // Runs a nested message loop. - return Send(message); } void RenderViewImpl::OnForceRedraw(int id) { @@ -1584,7 +1508,7 @@ WebView* RenderViewImpl::createView(WebLocalFrame* creator, WebNavigationPolicy policy, bool suppress_opener) { ViewHostMsg_CreateWindow_Params params; - params.opener_id = routing_id(); + params.opener_id = GetRoutingID(); params.user_gesture = WebUserGestureIndicator::isProcessingUserGesture(); if (GetContentClient()->renderer()->AllowPopup()) params.user_gesture = true; @@ -1609,10 +1533,11 @@ WebView* RenderViewImpl::createView(WebLocalFrame* creator, params.opener_top_level_frame_url = creator->top()->document().url(); } else { params.opener_top_level_frame_url = - GURL(creator->top()->securityOrigin().toString()); + blink::WebStringToGURL(creator->top()->getSecurityOrigin().toString()); } - GURL security_url(creator->document().securityOrigin().toString()); + GURL security_url(blink::WebStringToGURL( + creator->document().getSecurityOrigin().toString())); if (!security_url.is_valid()) security_url = GURL(); params.opener_security_origin = security_url; @@ -1640,7 +1565,7 @@ WebView* RenderViewImpl::createView(WebLocalFrame* creator, // TODO(vangelis): Can we tell if the new view will be a background page? bool never_visible = false; - ViewMsg_Resize_Params initial_size = ViewMsg_Resize_Params(); + ResizeParams initial_size = ResizeParams(); initial_size.screen_info = screen_info_; // The initial hidden state for the RenderViewImpl here has to match what the @@ -1652,7 +1577,7 @@ WebView* RenderViewImpl::createView(WebLocalFrame* creator, RenderFrameImpl* creator_frame = RenderFrameImpl::FromWebFrame(creator); view_params.opener_frame_route_id = creator_frame->GetRoutingID(); - DCHECK_EQ(routing_id(), creator_frame->render_view()->GetRoutingID()); + DCHECK_EQ(GetRoutingID(), creator_frame->render_view()->GetRoutingID()); view_params.window_was_created_with_opener = true; view_params.renderer_preferences = renderer_preferences_; @@ -1677,14 +1602,11 @@ WebView* RenderViewImpl::createView(WebLocalFrame* creator, RenderViewImpl::Create(compositor_deps_, view_params, true); view->opened_by_user_gesture_ = params.user_gesture; - // Record whether the creator frame is trying to suppress the opener field. - view->opener_suppressed_ = params.opener_suppressed; - return view->webview(); } WebWidget* RenderViewImpl::createPopupMenu(blink::WebPopupType popup_type) { - RenderWidget* widget = RenderWidget::Create(routing_id(), compositor_deps_, + RenderWidget* widget = RenderWidget::Create(GetRoutingID(), compositor_deps_, popup_type, screen_info_); if (!widget) return NULL; @@ -1710,7 +1632,7 @@ void RenderViewImpl::saveImageFromDataURL(const blink::WebString& data_url) { // in order to send a larger data url to save a image for or . if (data_url.length() < kMaxLengthOfDataURLString) Send(new ViewHostMsg_SaveImageFromDataURL( - routing_id(), GetMainRenderFrame()->GetRoutingID(), data_url.utf8())); + GetRoutingID(), GetMainRenderFrame()->GetRoutingID(), data_url.utf8())); } bool RenderViewImpl::enumerateChosenDirectory( @@ -1719,7 +1641,7 @@ bool RenderViewImpl::enumerateChosenDirectory( int id = enumeration_completion_id_++; enumeration_completions_[id] = chooser_completion; return Send(new ViewHostMsg_EnumerateDirectory( - routing_id(), id, base::FilePath::FromUTF16Unsafe(path))); + GetRoutingID(), id, blink::WebStringToFilePath(path))); } void RenderViewImpl::FrameDidStartLoading(WebFrame* frame) { @@ -1741,7 +1663,7 @@ void RenderViewImpl::FrameDidStopLoading(WebFrame* frame) { } } -void RenderViewImpl::AttachWebFrameWidget(blink::WebWidget* frame_widget) { +void RenderViewImpl::AttachWebFrameWidget(blink::WebFrameWidget* frame_widget) { // The previous WebFrameWidget must already be detached by CloseForFrame(). DCHECK(!frame_widget_); frame_widget_ = frame_widget; @@ -1753,7 +1675,7 @@ void RenderViewImpl::SetZoomLevel(double zoom_level) { } void RenderViewImpl::didCancelCompositionOnSelectionChange() { - Send(new InputHostMsg_ImeCancelComposition(routing_id())); + Send(new InputHostMsg_ImeCancelComposition(GetRoutingID())); } bool RenderViewImpl::handleCurrentKeyboardEvent() { @@ -1799,7 +1721,7 @@ bool RenderViewImpl::runFileChooser( ipc_params.mode = FileChooserParams::Open; ipc_params.title = params.title; ipc_params.default_file_name = - base::FilePath::FromUTF16Unsafe(params.initialValue).BaseName(); + blink::WebStringToFilePath(params.initialValue).BaseName(); ipc_params.accept_types.reserve(params.acceptTypes.size()); for (size_t i = 0; i < params.acceptTypes.size(); ++i) ipc_params.accept_types.push_back(params.acceptTypes[i]); @@ -1848,18 +1770,18 @@ void RenderViewImpl::showValidationMessage( &wrapped_main_text, main_text_hint, &wrapped_sub_text, sub_text_hint); Send(new ViewHostMsg_ShowValidationMessage( - routing_id(), AdjustValidationMessageAnchor(anchor_in_viewport), + GetRoutingID(), AdjustValidationMessageAnchor(anchor_in_viewport), wrapped_main_text, wrapped_sub_text)); } void RenderViewImpl::hideValidationMessage() { - Send(new ViewHostMsg_HideValidationMessage(routing_id())); + Send(new ViewHostMsg_HideValidationMessage(GetRoutingID())); } void RenderViewImpl::moveValidationMessage( const blink::WebRect& anchor_in_viewport) { Send(new ViewHostMsg_MoveValidationMessage( - routing_id(), AdjustValidationMessageAnchor(anchor_in_viewport))); + GetRoutingID(), AdjustValidationMessageAnchor(anchor_in_viewport))); } void RenderViewImpl::setStatusText(const WebString& text) { @@ -1882,9 +1804,9 @@ void RenderViewImpl::UpdateTargetURL(const GURL& url, } else { // URLs larger than |kMaxURLChars| cannot be sent through IPC - // see |ParamTraits|. - if (latest_url.possibly_invalid_spec().size() > kMaxURLChars) + if (latest_url.possibly_invalid_spec().size() > url::kMaxURLChars) latest_url = GURL(); - Send(new ViewHostMsg_UpdateTargetURL(routing_id(), latest_url)); + Send(new ViewHostMsg_UpdateTargetURL(GetRoutingID(), latest_url)); target_url_ = latest_url; target_url_status_ = TARGET_INFLIGHT; } @@ -1945,10 +1867,12 @@ void RenderViewImpl::startDragging(WebLocalFrame* frame, WebDragOperationsMask mask, const WebImage& image, const WebPoint& webImageOffset) { + blink::WebRect offset_in_window(webImageOffset.x, webImageOffset.y, 0, 0); + ConvertViewportToWindowViaWidget(&offset_in_window); DropData drop_data(DropDataBuilder::Build(data)); drop_data.referrer_policy = frame->document().referrerPolicy(); - gfx::Vector2d imageOffset(webImageOffset.x, webImageOffset.y); - Send(new DragHostMsg_StartDragging(routing_id(), drop_data, mask, + gfx::Vector2d imageOffset(offset_in_window.x, offset_in_window.y); + Send(new DragHostMsg_StartDragging(GetRoutingID(), drop_data, mask, image.getSkBitmap(), imageOffset, possible_drag_event_info_)); } @@ -1958,11 +1882,11 @@ bool RenderViewImpl::acceptsLoadDrops() { } void RenderViewImpl::focusNext() { - Send(new ViewHostMsg_TakeFocus(routing_id(), false)); + Send(new ViewHostMsg_TakeFocus(GetRoutingID(), false)); } void RenderViewImpl::focusPrevious() { - Send(new ViewHostMsg_TakeFocus(routing_id(), true)); + Send(new ViewHostMsg_TakeFocus(GetRoutingID(), true)); } // TODO(esprehn): Blink only ever passes Elements, this should take WebElement. @@ -1975,11 +1899,11 @@ void RenderViewImpl::focusedNodeChanged(const WebNode& fromNode, if (!toNode.isNull() && toNode.isElementNode()) { WebElement element = const_cast(toNode).to(); blink::WebRect rect = element.boundsInViewport(); - convertViewportToWindow(&rect); + ConvertViewportToWindowViaWidget(&rect); node_bounds = gfx::Rect(rect); is_editable = element.isEditable(); } - Send(new ViewHostMsg_FocusedNodeChanged(routing_id(), is_editable, + Send(new ViewHostMsg_FocusedNodeChanged(GetRoutingID(), is_editable, node_bounds)); // TODO(estade): remove. @@ -2019,7 +1943,7 @@ void RenderViewImpl::didUpdateLayout() { } void RenderViewImpl::navigateBackForwardSoon(int offset) { - Send(new ViewHostMsg_GoToEntryAtOffset(routing_id(), offset)); + Send(new ViewHostMsg_GoToEntryAtOffset(GetRoutingID(), offset)); } int RenderViewImpl::historyBackListCount() { @@ -2038,7 +1962,7 @@ void RenderViewImpl::didFocus() { // move that code back to render_widget.cc if (WebUserGestureIndicator::isProcessingUserGesture() && !RenderThreadImpl::current()->layout_test_mode()) { - Send(new ViewHostMsg_Focus(routing_id())); + Send(new ViewHostMsg_Focus(GetRoutingID())); } } @@ -2064,7 +1988,7 @@ void RenderViewImpl::show(WebNavigationPolicy policy) { // NOTE: initial_rect_ may still have its default values at this point, but // that's okay. It'll be ignored if disposition is not NEW_POPUP, or the // browser process will impose a default position otherwise. - Send(new ViewHostMsg_ShowView(opener_id_, routing_id(), + Send(new ViewHostMsg_ShowView(opener_id_, GetRoutingID(), NavigationPolicyToDisposition(policy), initial_rect_, opened_by_user_gesture_)); SetPendingWindowRect(initial_rect_); @@ -2109,7 +2033,7 @@ void RenderViewImpl::didHandleGestureEvent( blink::WebTextInputType text_input_type = GetWebView()->textInputType(); Send(new ViewHostMsg_FocusedNodeTouched( - routing_id(), text_input_type != blink::WebTextInputTypeNone)); + GetRoutingID(), text_input_type != blink::WebTextInputTypeNone)); #endif } @@ -2120,7 +2044,7 @@ void RenderViewImpl::initializeLayerTreeView() { return; bool use_threaded_event_handling = true; -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MACOSX) // Disable threaded event handling if content is not handling the elastic // overscroll effect. This includes the cases where the elastic overscroll // effect is being handled by Blink (because of command line flags) and older @@ -2135,8 +2059,9 @@ void RenderViewImpl::initializeLayerTreeView() { render_thread ? render_thread->input_handler_manager() : NULL; if (input_handler_manager) { input_handler_manager->AddInputHandler( - routing_id(), rwc->GetInputHandler(), AsWeakPtr(), - webkit_preferences_.enable_scroll_animator); + GetRoutingID(), rwc->GetInputHandler(), AsWeakPtr(), + webkit_preferences_.enable_scroll_animator, + UseGestureBasedWheelScrolling()); } } } @@ -2172,14 +2097,14 @@ const std::string& RenderViewImpl::GetAcceptLanguages() const { return renderer_preferences_.accept_languages; } -void RenderViewImpl::convertViewportToWindow(blink::WebRect* rect) { - RenderWidget::convertViewportToWindow(rect); +void RenderViewImpl::ConvertViewportToWindowViaWidget(blink::WebRect* rect) { + convertViewportToWindow(rect); } gfx::RectF RenderViewImpl::ElementBoundsInWindow( const blink::WebElement& element) { blink::WebRect bounding_box_in_window = element.boundsInViewport(); - convertViewportToWindow(&bounding_box_in_window); + ConvertViewportToWindowViaWidget(&bounding_box_in_window); return gfx::RectF(bounding_box_in_window); } @@ -2187,6 +2112,13 @@ float RenderViewImpl::GetDeviceScaleFactorForTest() const { return device_scale_factor_; } +gfx::Point RenderViewImpl::ConvertWindowPointToViewport( + const gfx::Point& point) { + blink::WebFloatRect point_in_viewport(point.x(), point.y(), 0, 0); + convertWindowToViewport(&point_in_viewport); + return gfx::Point(point_in_viewport.x, point_in_viewport.y); +} + void RenderViewImpl::didChangeIcon(WebLocalFrame* frame, WebIconURL::Type icon_type) { if (frame->parent()) @@ -2215,20 +2147,10 @@ void RenderViewImpl::CheckPreferredSize() { return; preferred_size_ = size; - Send(new ViewHostMsg_DidContentsPreferredSizeChange(routing_id(), + Send(new ViewHostMsg_DidContentsPreferredSizeChange(GetRoutingID(), preferred_size_)); } -void RenderViewImpl::SendFindReply(int request_id, - int match_count, - int ordinal, - const WebRect& selection_rect, - bool final_status_update) { - Send(new ViewHostMsg_Find_Reply(routing_id(), request_id, match_count, - selection_rect, ordinal, - final_status_update)); -} - blink::WebString RenderViewImpl::acceptLanguages() { return WebString::fromUTF8(renderer_preferences_.accept_languages); } @@ -2239,6 +2161,10 @@ bool RenderViewImpl::Send(IPC::Message* message) { return RenderWidget::Send(message); } +RenderWidget* RenderViewImpl::GetWidget() const { + return const_cast(static_cast(this)); +} + RenderFrameImpl* RenderViewImpl::GetMainRenderFrame() { return main_render_frame_; } @@ -2267,6 +2193,10 @@ blink::WebView* RenderViewImpl::GetWebView() { return webview(); } +blink::WebFrameWidget* RenderViewImpl::GetWebFrameWidget() { + return frame_widget_; +} + bool RenderViewImpl::ShouldDisplayScrollbars(int width, int height) const { return (!send_preferred_size_changes_ || (disable_scrollbars_size_limit_.width() <= width || @@ -2306,227 +2236,6 @@ blink::WebElement RenderViewImpl::GetFocusedElement() const { return WebElement(); } -blink::WebPlugin* RenderViewImpl::GetWebPluginForFind() { - if (!webview()) - return NULL; - - WebFrame* main_frame = webview()->mainFrame(); - if (main_frame->isWebLocalFrame() && - main_frame->document().isPluginDocument()) - return webview()->mainFrame()->document().to().plugin(); - -#if defined(ENABLE_PLUGINS) - if (plugin_find_handler_) - return plugin_find_handler_->container()->plugin(); -#endif - - return NULL; -} - -void RenderViewImpl::OnFind(int request_id, - const base::string16& search_text, - const WebFindOptions& options) { - DCHECK(!search_text.empty()); - - WebFrame* main_frame = webview()->mainFrame(); - blink::WebPlugin* plugin = GetWebPluginForFind(); - // Check if the plugin still exists in the document. - if (plugin) { - if (options.findNext) { - // Just navigate back/forward. - plugin->selectFindResult(options.forward); - } else { - if (!plugin->startFind( - search_text, options.matchCase, request_id)) { - // Send "no results". - SendFindReply(request_id, 0, 0, gfx::Rect(), true); - } - } - return; - } - - WebFrame* frame_after_main = main_frame->traverseNext(true); - WebFrame* focused_frame = webview()->focusedFrame(); - WebFrame* search_frame = focused_frame; // start searching focused frame. - - bool multi_frame = (frame_after_main != main_frame); - - // If we have multiple frames, we don't want to wrap the search within the - // frame, so we check here if we only have main_frame in the chain. - bool wrap_within_frame = !multi_frame; - - WebRect selection_rect; - bool result = false; - - // If something is selected when we start searching it means we cannot just - // increment the current match ordinal; we need to re-generate it. - WebRange current_selection = focused_frame->selectionRange(); - - do { - result = search_frame->find( - request_id, search_text, options, wrap_within_frame, &selection_rect); - - if (!result) { - // don't leave text selected as you move to the next frame. - search_frame->executeCommand(WebString::fromUTF8("Unselect"), - GetFocusedElement()); - - // Find the next frame, but skip the invisible ones. - do { - // What is the next frame to search? (we might be going backwards). Note - // that we specify wrap=true so that search_frame never becomes NULL. - search_frame = options.forward ? - search_frame->traverseNext(true) : - search_frame->traversePrevious(true); - } while (!search_frame->hasVisibleContent() && - search_frame != focused_frame); - - // Make sure selection doesn't affect the search operation in new frame. - search_frame->executeCommand(WebString::fromUTF8("Unselect"), - GetFocusedElement()); - - // If we have multiple frames and we have wrapped back around to the - // focused frame, we need to search it once more allowing wrap within - // the frame, otherwise it will report 'no match' if the focused frame has - // reported matches, but no frames after the focused_frame contain a - // match for the search word(s). - if (multi_frame && search_frame == focused_frame) { - result = search_frame->find( - request_id, search_text, options, true, // Force wrapping. - &selection_rect); - } - } - - webview()->setFocusedFrame(search_frame); - } while (!result && search_frame != focused_frame); - - if (options.findNext && current_selection.isNull()) { - // Force the main_frame to report the actual count. - main_frame->increaseMatchCount(0, request_id); - } else { - // If nothing is found, set result to "0 of 0", otherwise, set it to - // "-1 of 1" to indicate that we found at least one item, but we don't know - // yet what is active. - int ordinal = result ? -1 : 0; // -1 here means, we might know more later. - int match_count = result ? 1 : 0; // 1 here means possibly more coming. - - // If we find no matches then this will be our last status update. - // Otherwise the scoping effort will send more results. - bool final_status_update = !result; - - SendFindReply(request_id, match_count, ordinal, selection_rect, - final_status_update); - - // Scoping effort begins, starting with the mainframe. - search_frame = main_frame; - - main_frame->resetMatchCount(); - - do { - // Cancel all old scoping requests before starting a new one. - search_frame->cancelPendingScopingEffort(); - - // We don't start another scoping effort unless at least one match has - // been found. - if (result) { - // Start new scoping request. If the scoping function determines that it - // needs to scope, it will defer until later. - search_frame->scopeStringMatches(request_id, - search_text, - options, - true); // reset the tickmarks - } - - // Iterate to the next frame. The frame will not necessarily scope, for - // example if it is not visible. - search_frame = search_frame->traverseNext(true); - } while (search_frame != main_frame); - } -} - -void RenderViewImpl::OnStopFinding(StopFindAction action) { - WebView* view = webview(); - if (!view) - return; - - blink::WebPlugin* plugin = GetWebPluginForFind(); - if (plugin) { - plugin->stopFind(); - return; - } - - bool clear_selection = action == STOP_FIND_ACTION_CLEAR_SELECTION; - if (clear_selection) { - view->focusedFrame()->executeCommand(WebString::fromUTF8("Unselect"), - GetFocusedElement()); - } - - WebFrame* frame = view->mainFrame(); - while (frame) { - frame->stopFinding(clear_selection); - frame = frame->traverseNext(false); - } - - if (action == STOP_FIND_ACTION_ACTIVATE_SELECTION) { - WebFrame* focused_frame = view->focusedFrame(); - if (focused_frame) { - WebDocument doc = focused_frame->document(); - if (!doc.isNull()) { - WebElement element = doc.focusedElement(); - if (!element.isNull()) - element.simulateClick(); - } - } - } -} - -#if defined(OS_ANDROID) -void RenderViewImpl::OnActivateNearestFindResult(int request_id, - float x, float y) { - if (!webview()) - return; - - WebFrame* main_frame = webview()->mainFrame(); - WebRect selection_rect; - int ordinal = main_frame->selectNearestFindMatch(WebFloatPoint(x, y), - &selection_rect); - if (ordinal == -1) { - // Something went wrong, so send a no-op reply (force the main_frame to - // report the current match count) in case the host is waiting for a - // response due to rate-limiting). - main_frame->increaseMatchCount(0, request_id); - return; - } - - SendFindReply(request_id, - -1 /* number_of_matches */, - ordinal, - selection_rect, - true /* final_update */); -} - -void RenderViewImpl::OnFindMatchRects(int current_version) { - if (!webview()) - return; - - WebFrame* main_frame = webview()->mainFrame(); - std::vector match_rects; - - int rects_version = main_frame->findMatchMarkersVersion(); - if (current_version != rects_version) { - WebVector web_match_rects; - main_frame->findMatchRects(web_match_rects); - match_rects.reserve(web_match_rects.size()); - for (size_t i = 0; i < web_match_rects.size(); ++i) - match_rects.push_back(gfx::RectF(web_match_rects[i])); - } - - gfx::RectF active_rect = main_frame->activeFindMatchRect(); - Send(new ViewHostMsg_FindMatchRects_Reply(routing_id(), rects_version, - match_rects, active_rect)); -} -#endif - void RenderViewImpl::OnSetPageScale(float page_scale_factor) { if (!webview()) return; @@ -2596,14 +2305,15 @@ void RenderViewImpl::OnAllowBindings(int enabled_bindings_flags) { // WebUIExtensionData deletes itself when we're destroyed. new WebUIExtensionData(this); - if (main_render_frame_) - main_render_frame_->EnableMojoBindings(); } enabled_bindings_ |= enabled_bindings_flags; // Keep track of the total bindings accumulated in this process. RenderProcess::current()->AddBindings(enabled_bindings_flags); + + if (main_render_frame_) + main_render_frame_->MaybeEnableMojoBindings(); } void RenderViewImpl::OnDragTargetDragEnter(const DropData& drop_data, @@ -2613,12 +2323,12 @@ void RenderViewImpl::OnDragTargetDragEnter(const DropData& drop_data, int key_modifiers) { WebDragOperation operation = webview()->dragTargetDragEnter( DropDataToWebDragData(drop_data), - client_point, + ConvertWindowPointToViewport(client_point), screen_point, ops, key_modifiers); - Send(new DragHostMsg_UpdateDragCursor(routing_id(), operation)); + Send(new DragHostMsg_UpdateDragCursor(GetRoutingID(), operation)); } void RenderViewImpl::OnDragTargetDragOver(const gfx::Point& client_point, @@ -2626,12 +2336,12 @@ void RenderViewImpl::OnDragTargetDragOver(const gfx::Point& client_point, WebDragOperationsMask ops, int key_modifiers) { WebDragOperation operation = webview()->dragTargetDragOver( - client_point, + ConvertWindowPointToViewport(client_point), screen_point, ops, key_modifiers); - Send(new DragHostMsg_UpdateDragCursor(routing_id(), operation)); + Send(new DragHostMsg_UpdateDragCursor(GetRoutingID(), operation)); } void RenderViewImpl::OnDragTargetDragLeave() { @@ -2641,13 +2351,15 @@ void RenderViewImpl::OnDragTargetDragLeave() { void RenderViewImpl::OnDragTargetDrop(const gfx::Point& client_point, const gfx::Point& screen_point, int key_modifiers) { - webview()->dragTargetDrop(client_point, screen_point, key_modifiers); + webview()->dragTargetDrop( + ConvertWindowPointToViewport(client_point), screen_point, key_modifiers); } void RenderViewImpl::OnDragSourceEnded(const gfx::Point& client_point, const gfx::Point& screen_point, WebDragOperation op) { - webview()->dragSourceEndedAt(client_point, screen_point, op); + webview()->dragSourceEndedAt( + ConvertWindowPointToViewport(client_point), screen_point, op); } void RenderViewImpl::OnDragSourceSystemDragEnded() { @@ -2705,7 +2417,7 @@ void RenderViewImpl::OnFileChooserResponse( // If there are more pending file chooser requests, schedule one now. if (!file_chooser_completions_.empty()) { Send(new ViewHostMsg_RunFileChooser( - routing_id(), file_chooser_completions_.front()->params)); + GetRoutingID(), file_chooser_completions_.front()->params)); } } @@ -2733,15 +2445,18 @@ void RenderViewImpl::OnDisableAutoResize(const gfx::Size& new_size) { webview()->disableAutoResizeMode(); if (!new_size.IsEmpty()) { - Resize(new_size, - physical_backing_size_, - top_controls_shrink_blink_size_, - top_controls_height_, - visible_viewport_size_, - resizer_rect_, - is_fullscreen_granted_, - display_mode_, - NO_RESIZE_ACK); + ResizeParams resize_params; + resize_params.new_size = new_size; + resize_params.physical_backing_size = physical_backing_size_; + resize_params.top_controls_shrink_blink_size = + top_controls_shrink_blink_size_; + resize_params.top_controls_height = top_controls_height_; + resize_params.visible_viewport_size = visible_viewport_size_; + resize_params.resizer_rect = resizer_rect_; + resize_params.is_fullscreen_granted = is_fullscreen_granted(); + resize_params.display_mode = display_mode_; + resize_params.needs_resize_ack = false; + Resize(resize_params); } } @@ -2820,11 +2535,6 @@ void RenderViewImpl::OnPluginActionAt(const gfx::Point& location, webview()->performPluginAction(action, location); } -void RenderViewImpl::OnSuppressDialogsUntilSwapOut() { - // Don't show any more dialogs until we finish OnSwapOut. - suppress_dialogs_until_swap_out_ = true; -} - void RenderViewImpl::OnClosePage() { FOR_EACH_OBSERVER(RenderViewObserver, observers_, ClosePage()); // TODO(creis): We'd rather use webview()->Close() here, but that currently @@ -2836,12 +2546,12 @@ void RenderViewImpl::OnClosePage() { // http://b/issue?id=753080. webview()->mainFrame()->dispatchUnloadEvent(); - Send(new ViewHostMsg_ClosePage_ACK(routing_id())); + Send(new ViewHostMsg_ClosePage_ACK(GetRoutingID())); } void RenderViewImpl::OnClose() { if (closing_) - RenderThread::Get()->Send(new ViewHostMsg_Close_ACK(routing_id())); + RenderThread::Get()->Send(new ViewHostMsg_Close_ACK(GetRoutingID())); RenderWidget::OnClose(); } @@ -2863,7 +2573,7 @@ void RenderViewImpl::OnMoveOrResizeStarted() { webview()->hidePopups(); } -void RenderViewImpl::OnResize(const ViewMsg_Resize_Params& params) { +void RenderViewImpl::OnResize(const ResizeParams& params) { TRACE_EVENT0("renderer", "RenderViewImpl::OnResize"); if (webview()) { webview()->hidePopups(); @@ -2886,7 +2596,7 @@ void RenderViewImpl::OnResize(const ViewMsg_Resize_Params& params) { has_scrolled_focused_editable_node_into_rect_ = false; } -void RenderViewImpl::DidInitiatePaint() { +void RenderViewImpl::RenderWidgetDidCommitAndDrawCompositorFrame() { #if defined(ENABLE_PLUGINS) // Notify all instances that we painted. The same caveats apply as for // ViewFlushedPaint regarding instances closing themselves, so we take @@ -2899,7 +2609,7 @@ void RenderViewImpl::DidInitiatePaint() { #endif } -void RenderViewImpl::DidFlushPaint() { +void RenderViewImpl::RenderWidgetDidFlushPaint() { // If the RenderWidget is closing down then early-exit, otherwise we'll crash. // See crbug.com/112921. if (!webview()) @@ -2946,31 +2656,14 @@ void RenderViewImpl::DidFlushPaint() { } } -gfx::Vector2d RenderViewImpl::GetScrollOffset() { - WebFrame* main_frame = webview()->mainFrame(); - for (WebFrame* frame = main_frame; frame; - frame = frame->traverseNext(false)) { - // TODO(nasko): This is a hack for the case in which the top-level - // frame is being rendered in another process. It will not - // behave correctly for out of process iframes. - if (frame->isWebLocalFrame()) { - main_frame = frame; - break; - } - } - - WebSize scroll_offset = main_frame->scrollOffset(); - return gfx::Vector2d(scroll_offset.width, scroll_offset.height); -} - void RenderViewImpl::OnClearFocusedElement() { if (webview()) webview()->clearFocusedElement(); } void RenderViewImpl::OnSetBackgroundOpaque(bool opaque) { - if (webview()) - webview()->setIsTransparent(!opaque); + if (frame_widget_) + frame_widget_->setIsTransparent(!opaque); if (compositor_) compositor_->setHasTransparentBackground(!opaque); } @@ -2978,52 +2671,8 @@ void RenderViewImpl::OnSetBackgroundOpaque(bool opaque) { void RenderViewImpl::OnSetActive(bool active) { if (webview()) webview()->setIsActive(active); - -#if defined(ENABLE_PLUGINS) && defined(OS_MACOSX) - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->SetWindowFocus(active); - } -#endif -} - -#if defined(OS_MACOSX) -void RenderViewImpl::OnSetWindowVisibility(bool visible) { -#if defined(ENABLE_PLUGINS) - // Inform plugins that their container has changed visibility. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->SetContainerVisibility(visible); - } -#endif -} - -void RenderViewImpl::OnWindowFrameChanged(const gfx::Rect& window_frame, - const gfx::Rect& view_frame) { -#if defined(ENABLE_PLUGINS) - // Inform plugins that their window's frame has changed. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->WindowFrameChanged(window_frame, view_frame); - } -#endif } -void RenderViewImpl::OnPluginImeCompositionCompleted(const base::string16& text, - int plugin_id) { - // WebPluginDelegateProxy is responsible for figuring out if this event - // applies to it or not, so inform all the delegates. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->ImeCompositionCompleted(text, plugin_id); - } -} -#endif // OS_MACOSX - void RenderViewImpl::CloseForFrame() { DCHECK(frame_widget_); frame_widget_->close(); @@ -3035,8 +2684,8 @@ void RenderViewImpl::Close() { WebView* doomed = webview(); RenderWidget::Close(); g_view_map.Get().erase(doomed); - g_routing_id_view_map.Get().erase(routing_id()); - RenderThread::Get()->Send(new ViewHostMsg_Close_ACK(routing_id())); + g_routing_id_view_map.Get().erase(GetRoutingID()); + RenderThread::Get()->Send(new ViewHostMsg_Close_ACK(GetRoutingID())); } void RenderViewImpl::OnWasHidden() { @@ -3056,15 +2705,6 @@ void RenderViewImpl::OnWasHidden() { for (PepperPluginSet::iterator i = active_pepper_instances_.begin(); i != active_pepper_instances_.end(); ++i) (*i)->PageVisibilityChanged(false); - -#if defined(OS_MACOSX) - // Inform NPAPI plugins that their container is no longer visible. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->SetContainerVisibility(false); - } -#endif // OS_MACOSX #endif // ENABLE_PLUGINS } @@ -3084,15 +2724,6 @@ void RenderViewImpl::OnWasShown(bool needs_repainting, for (PepperPluginSet::iterator i = active_pepper_instances_.begin(); i != active_pepper_instances_.end(); ++i) (*i)->PageVisibilityChanged(true); - -#if defined(OS_MACOSX) - // Inform NPAPI plugins that their container is now visible. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { - (*plugin_it)->SetContainerVisibility(true); - } -#endif // OS_MACOSX #endif // ENABLE_PLUGINS } @@ -3116,20 +2747,6 @@ void RenderViewImpl::SetFocus(bool enable) { RenderWidget::OnSetFocus(enable); #if defined(ENABLE_PLUGINS) - if (webview() && webview()->isActive()) { - // Notify all NPAPI plugins. - std::set::iterator plugin_it; - for (plugin_it = plugin_delegates_.begin(); - plugin_it != plugin_delegates_.end(); ++plugin_it) { -#if defined(OS_MACOSX) - // RenderWidget's call to setFocus can cause the underlying webview's - // activation state to change just like a call to setIsActive. - if (enable) - (*plugin_it)->SetWindowFocus(true); -#endif - (*plugin_it)->SetContentAreaFocus(enable); - } - } // Notify all Pepper plugins. for (PepperPluginSet::iterator i = active_pepper_instances_.begin(); i != active_pepper_instances_.end(); ++i) @@ -3143,6 +2760,7 @@ void RenderViewImpl::SetFocus(bool enable) { void RenderViewImpl::OnImeSetComposition( const base::string16& text, const std::vector& underlines, + const gfx::Range& replacement_range, int selection_start, int selection_end) { #if defined(ENABLE_PLUGINS) @@ -3151,35 +2769,20 @@ void RenderViewImpl::OnImeSetComposition( text, underlines, selection_start, selection_end); return; } - -#if defined(OS_WIN) - // When a plugin has focus, we create platform-specific IME data used by - // our IME emulator and send it directly to the focused plugin, i.e. we - // bypass WebKit. (WebPluginDelegate dispatches this IME data only when its - // instance ID is the same one as the specified ID.) - if (focused_plugin_id_ >= 0) { - std::vector clauses; - std::vector target; - for (size_t i = 0; i < underlines.size(); ++i) { - clauses.push_back(underlines[i].startOffset); - clauses.push_back(underlines[i].endOffset); - if (underlines[i].thick) { - target.clear(); - target.push_back(underlines[i].startOffset); - target.push_back(underlines[i].endOffset); - } - } - std::set::iterator it; - for (it = plugin_delegates_.begin(); it != plugin_delegates_.end(); ++it) { - (*it)->ImeCompositionUpdated(text, clauses, target, selection_end, - focused_plugin_id_); +#endif // ENABLE_PLUGINS + if (replacement_range.IsValid() && webview()) { + // Select the text in |replacement_range|, it will then be replaced by + // text added by the call to RenderWidget::OnImeSetComposition(). + if (WebLocalFrame* frame = webview()->focusedFrame()->toWebLocalFrame()) { + WebRange webrange = WebRange::fromDocumentRange( + frame, replacement_range.start(), replacement_range.length()); + if (!webrange.isNull()) + frame->selectRange(webrange); } - return; } -#endif // OS_WIN -#endif // ENABLE_PLUGINS RenderWidget::OnImeSetComposition(text, underlines, + replacement_range, selection_start, selection_end); } @@ -3194,20 +2797,6 @@ void RenderViewImpl::OnImeConfirmComposition( text, replacement_range, keep_selection); return; } -#if defined(OS_WIN) - // Same as OnImeSetComposition(), we send the text from IMEs directly to - // plugins. When we send IME text directly to plugins, we should not send - // it to WebKit to prevent WebKit from controlling IMEs. - // TODO(thakis): Honor |replacement_range| for plugins? - if (focused_plugin_id_ >= 0) { - std::set::iterator it; - for (it = plugin_delegates_.begin(); - it != plugin_delegates_.end(); ++it) { - (*it)->ImeCompositionCompleted(text, focused_plugin_id_); - } - return; - } -#endif // OS_WIN #endif // ENABLE_PLUGINS if (replacement_range.IsValid() && webview()) { // Select the text in |replacement_range|, it will then be replaced by @@ -3224,20 +2813,18 @@ void RenderViewImpl::OnImeConfirmComposition( keep_selection); } -bool RenderViewImpl::SetDeviceColorProfile( +void RenderViewImpl::RenderWidgetDidSetColorProfile( const std::vector& profile) { - bool changed = RenderWidget::SetDeviceColorProfile(profile); - if (changed && webview()) { - WebVector colorProfile = profile; - webview()->setDeviceColorProfile(colorProfile); - } - return changed; -} + if (webview()) { + bool was_reset = (profile.size() == 1 && profile[0] == '0'); -void RenderViewImpl::ResetDeviceColorProfileForTesting() { - RenderWidget::ResetDeviceColorProfileForTesting(); - if (webview()) - webview()->resetDeviceColorProfileForTesting(); + if (was_reset) { + webview()->resetDeviceColorProfileForTesting(); + } else { + WebVector colorProfile = profile; + webview()->setDeviceColorProfile(colorProfile); + } + } } ui::TextInputType RenderViewImpl::GetTextInputType() { @@ -3256,7 +2843,7 @@ void RenderViewImpl::GetSelectionBounds(gfx::Rect* start, gfx::Rect* end) { // use the caret position as an empty range for now. It will be updated // after Pepper API equips features related to surrounding text retrieval. blink::WebRect caret(focused_pepper_plugin_->GetCaretBounds()); - convertViewportToWindow(&caret); + ConvertViewportToWindowViaWidget(&caret); *start = caret; *end = caret; return; @@ -3297,7 +2884,7 @@ void RenderViewImpl::GetCompositionCharacterBounds( bounds_in_window->clear(); return; } - convertViewportToWindow(&webrect); + ConvertViewportToWindowViaWidget(&webrect); bounds_in_window->push_back(webrect); } } @@ -3320,7 +2907,7 @@ bool RenderViewImpl::CanComposeInline() { } void RenderViewImpl::DidCompletePageScaleAnimation() { - FocusChangeComplete(); + GetWidget()->FocusChangeComplete(); } void RenderViewImpl::OnDeviceScaleFactorChanged() { @@ -3361,7 +2948,7 @@ bool RenderViewImpl::ScheduleFileChooser( make_scoped_ptr(new PendingFileChooser(params, completion))); if (file_chooser_completions_.size() == 1) { // Actually show the browse dialog when this is the first request. - Send(new ViewHostMsg_RunFileChooser(routing_id(), params)); + Send(new ViewHostMsg_RunFileChooser(GetRoutingID(), params)); } return true; } @@ -3381,7 +2968,7 @@ void RenderViewImpl::zoomLimitsChanged(double minimum_level, int maximum_percent = round( ZoomLevelToZoomFactor(maximum_level) * 100); - Send(new ViewHostMsg_UpdateZoomLimits(routing_id(), minimum_percent, + Send(new ViewHostMsg_UpdateZoomLimits(GetRoutingID(), minimum_percent, maximum_percent)); } @@ -3394,7 +2981,7 @@ void RenderViewImpl::zoomLevelChanged() { // Tell the browser which url got zoomed so it can update the menu and the // saved values if necessary Send(new ViewHostMsg_DidZoomURL( - routing_id(), zoom_level, + GetRoutingID(), zoom_level, GURL(webview()->mainFrame()->document().url()))); } } @@ -3403,7 +2990,7 @@ void RenderViewImpl::pageScaleFactorChanged() { if (!webview()) return; - Send(new ViewHostMsg_PageScaleFactorChanged(routing_id(), + Send(new ViewHostMsg_PageScaleFactorChanged(GetRoutingID(), webview()->pageScaleFactor())); } @@ -3489,7 +3076,7 @@ void RenderViewImpl::LaunchAndroidContentIntent(const GURL& intent, ScheduleComposite(); if (!intent.is_empty()) { - Send(new ViewHostMsg_StartContentIntent(routing_id(), intent, + Send(new ViewHostMsg_StartContentIntent(GetRoutingID(), intent, is_main_frame)); } } @@ -3522,15 +3109,6 @@ void RenderViewImpl::OnShowContextMenu( has_host_context_menu_location_ = false; } -void RenderViewImpl::OnEnableViewSourceMode() { - if (!webview()) - return; - WebFrame* main_frame = webview()->mainFrame(); - if (!main_frame) - return; - main_frame->enableViewSourceMode(true); -} - #if defined(OS_ANDROID) || defined(USE_AURA) bool RenderViewImpl::didTapMultipleTargets( const WebSize& inner_viewport_offset, @@ -3602,7 +3180,7 @@ bool RenderViewImpl::didTapMultipleTargets( ClientRectToPhysicalWindowRect(gfx::RectF(zoom_rect_in_screen))); Send(new ViewHostMsg_ShowDisambiguationPopup( - routing_id(), physical_window_zoom_rect, canvas_size, + GetRoutingID(), physical_window_zoom_rect, canvas_size, shared_bitmap->id())); cc::SharedBitmapId id = shared_bitmap->id(); disambiguation_bitmaps_[id] = shared_bitmap.release(); @@ -3637,7 +3215,7 @@ void RenderViewImpl::SetFocusAndActivateForTesting(bool enable) { } void RenderViewImpl::SetDeviceScaleFactorForTesting(float factor) { - ViewMsg_Resize_Params params; + ResizeParams params; params.screen_info = screen_info_; params.screen_info.deviceScaleFactor = factor; params.new_size = size(); @@ -3651,11 +3229,6 @@ void RenderViewImpl::SetDeviceScaleFactorForTesting(float factor) { OnResize(params); } -void RenderViewImpl::SetDeviceColorProfileForTesting( - const std::vector& color_profile) { - SetDeviceColorProfile(color_profile); -} - void RenderViewImpl::ForceResizeForTesting(const gfx::Size& new_size) { gfx::Rect new_window_rect(rootWindowRect().x, rootWindowRect().y, @@ -3692,7 +3265,7 @@ void RenderViewImpl::DidCommitCompositorFrame() { void RenderViewImpl::SendUpdateFaviconURL(const std::vector& urls) { if (!urls.empty()) - Send(new ViewHostMsg_UpdateFaviconURL(routing_id(), urls)); + Send(new ViewHostMsg_UpdateFaviconURL(GetRoutingID(), urls)); } void RenderViewImpl::DidStopLoadingIcons() { diff --git a/chromium/content/renderer/render_view_impl.h b/chromium/content/renderer/render_view_impl.h index b6e54b8cf9c..71f89fb01af 100644 --- a/chromium/content/renderer/render_view_impl.h +++ b/chromium/content/renderer/render_view_impl.h @@ -36,17 +36,18 @@ #include "content/public/common/page_zoom.h" #include "content/public/common/referrer.h" #include "content/public/common/renderer_preferences.h" -#include "content/public/common/stop_find_action.h" #include "content/public/common/top_controls_state.h" #include "content/public/common/web_preferences.h" #include "content/public/renderer/render_view.h" #include "content/renderer/mouse_lock_dispatcher.h" #include "content/renderer/render_frame_impl.h" #include "content/renderer/render_widget.h" +#include "content/renderer/render_widget_owner_delegate.h" #include "content/renderer/stats_collection_observer.h" #include "ipc/ipc_platform_file.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/WebKit/public/platform/WebPageVisibilityState.h" +#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebAXObject.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebDataSource.h" @@ -57,7 +58,6 @@ #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebNavigationType.h" #include "third_party/WebKit/public/web/WebNode.h" -#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebViewClient.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/geometry/rect.h" @@ -80,7 +80,6 @@ class SkBitmap; struct PP_NetAddress_Private; struct ViewMsg_New_Params; -struct ViewMsg_Resize_Params; struct ViewMsg_StopFinding_Params; namespace base { @@ -109,7 +108,6 @@ class WebURLRequest; struct WebActiveWheelFlingParameters; struct WebDateTimeChooserParams; struct WebFileChooserParams; -struct WebFindOptions; struct WebMediaPlayerAction; struct WebPluginAction; struct WebPoint; @@ -139,6 +137,7 @@ struct FaviconURL; struct FileChooserParams; struct FileChooserFileInfo; struct RenderViewImplParams; +struct ResizeParams; #if defined(OS_ANDROID) class WebMediaPlayerProxyAndroid; @@ -156,6 +155,7 @@ class WebMediaPlayerProxyAndroid; class CONTENT_EXPORT RenderViewImpl : public RenderWidget, NON_EXPORTED_BASE(public blink::WebViewClient), + public RenderWidgetOwnerDelegate, public RenderView, public base::SupportsWeakPtr { public: @@ -243,19 +243,11 @@ class CONTENT_EXPORT RenderViewImpl // FocusController. void SetFocus(bool enable); - void AttachWebFrameWidget(blink::WebWidget* frame_widget); + void AttachWebFrameWidget(blink::WebFrameWidget* frame_widget); // Plugin-related functions -------------------------------------------------- #if defined(ENABLE_PLUGINS) - // Get/set the plugin which will be used as to handle document find requests. - void set_plugin_find_handler(PepperPluginInstanceImpl* plugin) { - plugin_find_handler_ = plugin; - } - PepperPluginInstanceImpl* plugin_find_handler() { - return plugin_find_handler_; - } - PepperPluginInstanceImpl* focused_pepper_plugin() { return focused_pepper_plugin_; } @@ -266,16 +258,6 @@ class CONTENT_EXPORT RenderViewImpl pepper_last_mouse_event_target_ = plugin; } -#if defined(OS_MACOSX) || defined(OS_WIN) - // Informs the render view that the given plugin has gained or lost focus. - void PluginFocusChanged(bool focused, int plugin_id); -#endif - -#if defined(OS_MACOSX) - // Starts plugin IME. - void StartPluginIme(); -#endif - // Indicates that the given instance has been created. void PepperInstanceCreated(PepperPluginInstanceImpl* instance); @@ -286,9 +268,6 @@ class CONTENT_EXPORT RenderViewImpl // Notification that the given plugin is focused or unfocused. void PepperFocusChanged(PepperPluginInstanceImpl* instance, bool focused); - - void RegisterPluginDelegate(WebPluginDelegateProxy* delegate); - void UnregisterPluginDelegate(WebPluginDelegateProxy* delegate); #endif // ENABLE_PLUGINS void TransferActiveWheelFlingAnimation( @@ -315,10 +294,6 @@ class CONTENT_EXPORT RenderViewImpl // Change the device scale factor and force the compositor to resize. void SetDeviceScaleFactorForTesting(float factor); - // Change the device ICC color profile while running a layout test. - void SetDeviceColorProfileForTesting(const std::vector& color_profile); - void ResetDeviceColorProfileForTesting() override; - // Used to force the size of a window when running layout tests. void ForceResizeForTesting(const gfx::Size& new_size); @@ -329,15 +304,6 @@ class CONTENT_EXPORT RenderViewImpl const gfx::Size& max_size); void DisableAutoResizeForTesting(const gfx::Size& new_size); - // RenderWidgetInputHandlerDelegate implementation --------------------------- - - // Most methods are handled by RenderWidget - void FocusChangeComplete() override; - bool HasTouchEventHandlersAt(const gfx::Point& point) const override; - void OnDidHandleKeyEvent() override; - bool WillHandleGestureEvent(const blink::WebGestureEvent& event) override; - bool WillHandleMouseEvent(const blink::WebMouseEvent& event) override; - // IPC::Listener implementation ---------------------------------------------- bool OnMessageReceived(const IPC::Message& msg) override; @@ -439,6 +405,7 @@ class CONTENT_EXPORT RenderViewImpl // RenderView implementation ------------------------------------------------- bool Send(IPC::Message* message) override; + RenderWidget* GetWidget() const override; RenderFrameImpl* GetMainRenderFrame() override; int GetRoutingID() const override; gfx::Size GetSize() const override; @@ -446,6 +413,7 @@ class CONTENT_EXPORT RenderViewImpl WebPreferences& GetWebkitPreferences() override; void SetWebkitPreferences(const WebPreferences& preferences) override; blink::WebView* GetWebView() override; + blink::WebFrameWidget* GetWebFrameWidget() override; bool ShouldDisplayScrollbars(int width, int height) const override; int GetEnabledBindings() const override; bool GetContentStateImmediately() const override; @@ -463,10 +431,12 @@ class CONTENT_EXPORT RenderViewImpl TopControlsState current, bool animate) override; #endif - void convertViewportToWindow(blink::WebRect* rect) override; + void ConvertViewportToWindowViaWidget(blink::WebRect* rect) override; gfx::RectF ElementBoundsInWindow(const blink::WebElement& element) override; float GetDeviceScaleFactorForTest() const override; + gfx::Point ConvertWindowPointToViewport(const gfx::Point& point); + bool uses_temporary_zoom_level() const { return uses_temporary_zoom_level_; } // Please do not add your stuff randomly to the end here. If there is an @@ -477,10 +447,7 @@ class CONTENT_EXPORT RenderViewImpl // RenderWidget overrides: void CloseForFrame() override; void Close() override; - void OnResize(const ViewMsg_Resize_Params& params) override; - void DidInitiatePaint() override; - void DidFlushPaint() override; - gfx::Vector2d GetScrollOffset() override; + void OnResize(const ResizeParams& params) override; void OnSetFocus(bool enable) override; void OnWasHidden() override; void OnWasShown(bool needs_repainting, @@ -489,12 +456,12 @@ class CONTENT_EXPORT RenderViewImpl void OnImeSetComposition( const base::string16& text, const std::vector& underlines, + const gfx::Range& replacement_range, int selection_start, int selection_end) override; void OnImeConfirmComposition(const base::string16& text, const gfx::Range& replacement_range, bool keep_selection) override; - bool SetDeviceColorProfile(const std::vector& color_profile) override; void OnOrientationChange() override; ui::TextInputType GetTextInputType() override; void GetSelectionBounds(gfx::Rect* start, gfx::Rect* end) override; @@ -506,7 +473,6 @@ class CONTENT_EXPORT RenderViewImpl void DidCompletePageScaleAnimation() override; void OnDeviceScaleFactorChanged() override; - protected: RenderViewImpl(CompositorDependencies* compositor_deps, const ViewMsg_New_Params& params); @@ -556,7 +522,10 @@ class CONTENT_EXPORT RenderViewImpl FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, GetCompositionCharacterBoundsTest); FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, OnNavigationHttpPost); - FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, ScreenMetricsEmulation); + FRIEND_TEST_ALL_PREFIXES(RenderViewImplScaleFactorTest, + ScreenMetricsEmulationWithOriginalDSF1); + FRIEND_TEST_ALL_PREFIXES(RenderViewImplScaleFactorTest, + ScreenMetricsEmulationWithOriginalDSF2); FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel); #if defined(OS_MACOSX) @@ -587,6 +556,21 @@ class CONTENT_EXPORT RenderViewImpl CONNECTION_ERROR, }; + // RenderWidgetOwnerDelegate implementation ---------------------------------- + + void RenderWidgetDidSetColorProfile( + const std::vector& color_profile) override; + void RenderWidgetFocusChangeComplete() override; + bool DoesRenderWidgetHaveTouchEventHandlersAt( + const gfx::Point& point) const override; + void RenderWidgetDidHandleKeyEvent() override; + bool RenderWidgetWillHandleGestureEvent( + const blink::WebGestureEvent& event) override; + bool RenderWidgetWillHandleMouseEvent( + const blink::WebMouseEvent& event) override; + void RenderWidgetDidCommitAndDrawCompositorFrame() override; + void RenderWidgetDidFlushPaint() override; + // Old WebFrameClient implementations ---------------------------------------- // RenderViewImpl used to be a WebFrameClient, but now RenderFrameImpl is the @@ -607,9 +591,6 @@ class CONTENT_EXPORT RenderViewImpl blink::WebView* web_view, CompositorDependencies* compositor_deps); - // Sends a message and runs a nested message loop. - bool SendAndRunNestedMessageLoop(IPC::SyncMessage* message); - // IPC message handlers ------------------------------------------------------ // // The documentation for these functions should be in @@ -656,9 +637,6 @@ class CONTENT_EXPORT RenderViewImpl const std::vector& paths); void OnFileChooserResponse( const std::vector& files); - void OnFind(int request_id, - const base::string16&, - const blink::WebFindOptions&); void OnMediaPlayerActionAt(const gfx::Point& location, const blink::WebMediaPlayerAction& action); void OnPluginActionAt(const gfx::Point& location, @@ -676,19 +654,14 @@ class CONTENT_EXPORT RenderViewImpl void OnSetWebUIProperty(const std::string& name, const std::string& value); void OnSetZoomLevelForLoadingURL(const GURL& url, double zoom_level); void OnSetZoomLevelForView(bool uses_temporary_zoom_level, double level); - void OnStopFinding(StopFindAction action); - void OnSuppressDialogsUntilSwapOut(); void OnThemeChanged(); void OnUpdateTargetURLAck(); void OnUpdateWebPreferences(const WebPreferences& prefs); void OnSetPageScale(float page_scale_factor); void OnZoom(PageZoom zoom); - void OnEnableViewSourceMode(); void OnForceRedraw(int request_id); void OnSelectWordAroundCaret(); #if defined(OS_ANDROID) - void OnActivateNearestFindResult(int request_id, float x, float y); - void OnFindMatchRects(int current_version); void OnUndoScrollFocusedEditableNodeIntoRect(); void OnUpdateTopControlsState(bool enable_hiding, bool enable_showing, @@ -696,14 +669,11 @@ class CONTENT_EXPORT RenderViewImpl void OnExtractSmartClipData(const gfx::Rect& rect); #elif defined(OS_MACOSX) void OnGetRenderedText(); - void OnPluginImeCompositionCompleted(const base::string16& text, - int plugin_id); - void OnSetInLiveResize(bool in_live_resize); - void OnSetWindowVisibility(bool visible); - void OnWindowFrameChanged(const gfx::Rect& window_frame, - const gfx::Rect& view_frame); #endif + // Page message handlers ----------------------------------------------------- + void OnUpdateWindowScreenRect(gfx::Rect window_screen_rect); + // Adding a new message handler? Please add it in alphabetical order above // and put it in the same position in the .cc file. @@ -714,10 +684,6 @@ class CONTENT_EXPORT RenderViewImpl // Gets the currently focused element, if any. blink::WebElement GetFocusedElement() const; - // Called to get the WebPlugin to handle find requests in the document. - // Returns NULL if there is no such WebPlugin. - blink::WebPlugin* GetWebPluginForFind(); - #if defined(OS_ANDROID) // Launch an Android content intent with the given URL. void LaunchAndroidContentIntent(const GURL& intent_url, @@ -725,14 +691,6 @@ class CONTENT_EXPORT RenderViewImpl bool is_main_frame); #endif - // Sends a reply to the current find operation handling if it was a - // synchronous find request. - void SendFindReply(int request_id, - int match_count, - int ordinal, - const blink::WebRect& selection_rect, - bool final_status_update); - #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX)) void UpdateFontRenderingFromRendererPrefs(); #else @@ -765,12 +723,6 @@ class CONTENT_EXPORT RenderViewImpl // this object. base::ObserverList& observers() { return observers_; } - // TODO(nasko): Remove this method when we move to frame proxy objects, since - // the concept of swapped out will be eliminated. - void set_is_swapped_out(bool swapped_out) { - is_swapped_out_ = swapped_out; - } - NavigationGesture navigation_gesture() { return navigation_gesture_; } @@ -831,16 +783,6 @@ class CONTENT_EXPORT RenderViewImpl // Used for popups. bool opened_by_user_gesture_; - // Whether this RenderView was created by a frame that was suppressing its - // opener. If so, we may want to load pages in a separate process. See - // decidePolicyForNavigation for details. - bool opener_suppressed_; - - // Whether we must stop creating nested message loops for modal dialogs until - // OnSwapOut is called. This is necessary because modal dialogs have a - // PageGroupLoadDeferrer on the stack that interferes with swapping out. - bool suppress_dialogs_until_swap_out_; - // Timer used to delay the updating of nav state (see // StartNavStateSyncTimerIfNecessary). base::OneShotTimer nav_state_sync_timer_; @@ -936,7 +878,7 @@ class CONTENT_EXPORT RenderViewImpl // Note: RenderViewImpl is pulling double duty: it's the RenderWidget for the // "view", but it's also the RenderWidget for the main frame. - blink::WebWidget* frame_widget_; + blink::WebFrameWidget* frame_widget_; // The next group of objects all implement RenderViewObserver, so are deleted // along with the RenderView automatically. This is why we just store @@ -966,20 +908,7 @@ class CONTENT_EXPORT RenderViewImpl #endif // Plugins ------------------------------------------------------------------- - - // All the currently active plugin delegates for this RenderView; kept so - // that we can enumerate them to send updates about things like window - // location or tab focus and visibily. These are non-owning references. - std::set plugin_delegates_; - -#if defined(OS_WIN) - // The ID of the focused NPAPI plugin. - int focused_plugin_id_; -#endif - #if defined(ENABLE_PLUGINS) - PepperPluginInstanceImpl* plugin_find_handler_; - typedef std::set PepperPluginSet; PepperPluginSet active_pepper_instances_; diff --git a/chromium/content/renderer/render_view_mouse_lock_dispatcher.cc b/chromium/content/renderer/render_view_mouse_lock_dispatcher.cc index 25b4ccc4ff2..bdd747b044e 100644 --- a/chromium/content/renderer/render_view_mouse_lock_dispatcher.cc +++ b/chromium/content/renderer/render_view_mouse_lock_dispatcher.cc @@ -59,8 +59,8 @@ void RenderViewMouseLockDispatcher::OnLockMouseACK(bool succeeded) { // Mouse Capture is implicitly given for the duration of a drag event, and // sends all mouse events to the initial target of the drag. // If Lock is entered it supercedes any in progress Capture. - if (succeeded && render_view_impl_->webwidget()) - render_view_impl_->webwidget()->mouseCaptureLost(); + if (succeeded && render_view_impl_->GetWidget()->webwidget()) + render_view_impl_->GetWidget()->webwidget()->mouseCaptureLost(); } } // namespace content diff --git a/chromium/content/renderer/render_view_win.cc b/chromium/content/renderer/render_view_win.cc index 15cb47bf7cd..51f97f5dd7c 100644 --- a/chromium/content/renderer/render_view_win.cc +++ b/chromium/content/renderer/render_view_win.cc @@ -32,6 +32,11 @@ void RenderViewImpl::UpdateFontRenderingFromRendererPrefs() { blink::WebFontRendering::setLCDOrientation( gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation( prefs.subpixel_rendering)); + + blink::WebFontRendering::setAntialiasedTextEnabled( + prefs.should_antialias_text); + blink::WebFontRendering::setLCDTextEnabled(prefs.subpixel_rendering + != gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE); } void RenderViewImpl::UpdateThemePrefs() { diff --git a/chromium/content/renderer/render_widget.cc b/chromium/content/renderer/render_widget.cc index 0ddaf21eee1..b9ec5d3818a 100644 --- a/chromium/content/renderer/render_widget.cc +++ b/chromium/content/renderer/render_widget.cc @@ -9,6 +9,7 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -24,22 +25,24 @@ #include "cc/base/switches.h" #include "cc/debug/benchmark_instrumentation.h" #include "cc/output/output_surface.h" +#include "cc/scheduler/begin_frame_source.h" #include "cc/trees/layer_tree_host.h" #include "components/scheduler/renderer/render_widget_scheduling_state.h" #include "components/scheduler/renderer/renderer_scheduler.h" -#include "content/child/npapi/webplugin.h" #include "content/common/content_switches_internal.h" #include "content/common/gpu/client/context_provider_command_buffer.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" -#include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/common/gpu_process_launch_causes.h" #include "content/common/input/synthetic_gesture_packet.h" #include "content/common/input/web_input_event_traits.h" #include "content/common/input_messages.h" #include "content/common/swapped_out_messages.h" #include "content/common/view_messages.h" +#include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/context_menu_params.h" #include "content/renderer/cursor_utils.h" +#include "content/renderer/devtools/render_widget_screen_metrics_emulator.h" #include "content/renderer/external_popup_menu.h" #include "content/renderer/gpu/compositor_output_surface.h" #include "content/renderer/gpu/delegated_compositor_output_surface.h" @@ -55,6 +58,7 @@ #include "content/renderer/render_process.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/render_view_impl.h" +#include "content/renderer/render_widget_owner_delegate.h" #include "content/renderer/renderer_blink_platform_impl.h" #include "content/renderer/resizing_mode_selector.h" #include "ipc/ipc_sync_message.h" @@ -102,6 +106,10 @@ #include "content/renderer/mus/render_widget_mus_connection.h" #endif +#if defined(ENABLE_VULKAN) +#include "cc/output/vulkan_in_process_context_provider.h" +#endif + #include "third_party/WebKit/public/web/WebWidget.h" using blink::WebCompositionUnderline; @@ -128,6 +136,10 @@ using blink::WebTouchPoint; using blink::WebVector; using blink::WebWidget; +#define STATIC_ASSERT_ENUM(a, b) \ + static_assert(static_cast(a) == static_cast(b), \ + "mismatching enums: " #a) + namespace { typedef std::map TextInputModeMap; @@ -197,244 +209,6 @@ content::RenderWidgetInputHandlerDelegate* GetRenderWidgetInputHandlerDelegate( namespace content { -// RenderWidget::ScreenMetricsEmulator ---------------------------------------- - -class RenderWidget::ScreenMetricsEmulator { - public: - ScreenMetricsEmulator( - RenderWidget* widget, - const WebDeviceEmulationParams& params); - virtual ~ScreenMetricsEmulator(); - - // Scale and offset used to convert between host coordinates - // and webwidget coordinates. - float scale() { return scale_; } - gfx::PointF offset() { return offset_; } - gfx::Rect applied_widget_rect() const { return applied_widget_rect_; } - gfx::Rect original_screen_rect() const { return original_view_screen_rect_; } - const WebScreenInfo& original_screen_info() { return original_screen_info_; } - - void ChangeEmulationParams( - const WebDeviceEmulationParams& params); - - // The following methods alter handlers' behavior for messages related to - // widget size and position. - void OnResizeMessage(const ViewMsg_Resize_Params& params); - void OnUpdateScreenRectsMessage(const gfx::Rect& view_screen_rect, - const gfx::Rect& window_screen_rect); - void OnShowContextMenu(ContextMenuParams* params); - gfx::Rect AdjustValidationMessageAnchor(const gfx::Rect& anchor); - - private: - void Reapply(); - void Apply(bool top_controls_shrink_blink_size, - float top_controls_height, - gfx::Rect resizer_rect, - bool is_fullscreen_granted, - blink::WebDisplayMode display_mode); - - RenderWidget* widget_; - - // Parameters as passed by RenderWidget::EnableScreenMetricsEmulation. - WebDeviceEmulationParams params_; - - // The computed scale and offset used to fit widget into browser window. - float scale_; - gfx::PointF offset_; - - // Widget rect as passed to webwidget. - gfx::Rect applied_widget_rect_; - - // Original values to restore back after emulation ends. - gfx::Size original_size_; - gfx::Size original_physical_backing_size_; - gfx::Size original_visible_viewport_size_; - blink::WebScreenInfo original_screen_info_; - gfx::Rect original_view_screen_rect_; - gfx::Rect original_window_screen_rect_; -}; - -RenderWidget::ScreenMetricsEmulator::ScreenMetricsEmulator( - RenderWidget* widget, - const WebDeviceEmulationParams& params) - : widget_(widget), - params_(params), - scale_(1.f) { - original_size_ = widget_->size_; - original_physical_backing_size_ = widget_->physical_backing_size_; - original_visible_viewport_size_ = widget_->visible_viewport_size_; - original_screen_info_ = widget_->screen_info_; - original_view_screen_rect_ = widget_->view_screen_rect_; - original_window_screen_rect_ = widget_->window_screen_rect_; - Apply(widget_->top_controls_shrink_blink_size_, - widget_->top_controls_height_, - widget_->resizer_rect_, - widget_->is_fullscreen_granted_, - widget_->display_mode_); -} - -RenderWidget::ScreenMetricsEmulator::~ScreenMetricsEmulator() { - widget_->screen_info_ = original_screen_info_; - - widget_->SetDeviceScaleFactor(original_screen_info_.deviceScaleFactor); - widget_->SetScreenMetricsEmulationParameters(false, params_); - widget_->view_screen_rect_ = original_view_screen_rect_; - widget_->window_screen_rect_ = original_window_screen_rect_; - widget_->Resize(original_size_, - original_physical_backing_size_, - widget_->top_controls_shrink_blink_size_, - widget_->top_controls_height_, - original_visible_viewport_size_, - widget_->resizer_rect_, - widget_->is_fullscreen_granted_, - widget_->display_mode_, - NO_RESIZE_ACK); -} - -void RenderWidget::ScreenMetricsEmulator::ChangeEmulationParams( - const WebDeviceEmulationParams& params) { - params_ = params; - Reapply(); -} - -void RenderWidget::ScreenMetricsEmulator::Reapply() { - Apply(widget_->top_controls_shrink_blink_size_, - widget_->top_controls_height_, - widget_->resizer_rect_, - widget_->is_fullscreen_granted_, - widget_->display_mode_); -} - -void RenderWidget::ScreenMetricsEmulator::Apply( - bool top_controls_shrink_blink_size, - float top_controls_height, - gfx::Rect resizer_rect, - bool is_fullscreen_granted, - blink::WebDisplayMode display_mode) { - applied_widget_rect_.set_size(gfx::Size(params_.viewSize)); - if (!applied_widget_rect_.width()) - applied_widget_rect_.set_width(original_size_.width()); - if (!applied_widget_rect_.height()) - applied_widget_rect_.set_height(original_size_.height()); - - if (params_.fitToView && !original_size_.IsEmpty()) { - int original_width = std::max(original_size_.width(), 1); - int original_height = std::max(original_size_.height(), 1); - float width_ratio = - static_cast(applied_widget_rect_.width()) / original_width; - float height_ratio = - static_cast(applied_widget_rect_.height()) / original_height; - float ratio = std::max(1.0f, std::max(width_ratio, height_ratio)); - scale_ = 1.f / ratio; - - // Center emulated view inside available view space. - offset_.set_x( - (original_size_.width() - scale_ * applied_widget_rect_.width()) / 2); - offset_.set_y( - (original_size_.height() - scale_ * applied_widget_rect_.height()) / 2); - } else { - scale_ = params_.scale; - offset_.SetPoint(params_.offset.x, params_.offset.y); - if (!params_.viewSize.width && !params_.viewSize.height && scale_) { - applied_widget_rect_.set_size(gfx::ScaleToRoundedSize( - original_size_, 1.f / scale_)); - } - } - - if (params_.screenPosition == WebDeviceEmulationParams::Desktop) { - applied_widget_rect_.set_origin(original_view_screen_rect_.origin()); - widget_->screen_info_.rect = original_screen_info_.rect; - widget_->screen_info_.availableRect = original_screen_info_.availableRect; - widget_->window_screen_rect_ = original_window_screen_rect_; - } else { - applied_widget_rect_.set_origin(params_.viewPosition); - gfx::Rect screen_rect = applied_widget_rect_; - if (!params_.screenSize.isEmpty()) { - screen_rect = - gfx::Rect(0, 0, params_.screenSize.width, params_.screenSize.height); - } - widget_->screen_info_.rect = screen_rect; - widget_->screen_info_.availableRect = screen_rect; - widget_->window_screen_rect_ = applied_widget_rect_; - } - - float applied_device_scale_factor = params_.deviceScaleFactor ? - params_.deviceScaleFactor : original_screen_info_.deviceScaleFactor; - widget_->screen_info_.deviceScaleFactor = applied_device_scale_factor; - - // Pass three emulation parameters to the blink side: - // - we keep the real device scale factor in compositor to produce sharp image - // even when emulating different scale factor; - // - in order to fit into view, WebView applies offset and scale to the - // root layer. - blink::WebDeviceEmulationParams modified_params = params_; - modified_params.deviceScaleFactor = original_screen_info_.deviceScaleFactor; - modified_params.offset = blink::WebFloatPoint(offset_.x(), offset_.y()); - modified_params.scale = scale_; - widget_->SetScreenMetricsEmulationParameters(true, modified_params); - - widget_->SetDeviceScaleFactor(applied_device_scale_factor); - widget_->view_screen_rect_ = applied_widget_rect_; - - gfx::Size physical_backing_size = gfx::ScaleToCeiledSize( - original_size_, original_screen_info_.deviceScaleFactor); - widget_->Resize(applied_widget_rect_.size(), - physical_backing_size, - top_controls_shrink_blink_size, - top_controls_height, - applied_widget_rect_.size(), - resizer_rect, - is_fullscreen_granted, - display_mode, - NO_RESIZE_ACK); -} - -void RenderWidget::ScreenMetricsEmulator::OnResizeMessage( - const ViewMsg_Resize_Params& params) { - bool need_ack = params.new_size != original_size_ && - !params.new_size.IsEmpty() && !params.physical_backing_size.IsEmpty(); - original_size_ = params.new_size; - original_physical_backing_size_ = params.physical_backing_size; - original_screen_info_ = params.screen_info; - original_visible_viewport_size_ = params.visible_viewport_size; - Apply(params.top_controls_shrink_blink_size, - params.top_controls_height, - params.resizer_rect, - params.is_fullscreen_granted, - params.display_mode); - - if (need_ack) { - widget_->set_next_paint_is_resize_ack(); - if (widget_->compositor_) - widget_->compositor_->SetNeedsRedrawRect(gfx::Rect(widget_->size_)); - } -} - -void RenderWidget::ScreenMetricsEmulator::OnUpdateScreenRectsMessage( - const gfx::Rect& view_screen_rect, - const gfx::Rect& window_screen_rect) { - original_view_screen_rect_ = view_screen_rect; - original_window_screen_rect_ = window_screen_rect; - if (params_.screenPosition == WebDeviceEmulationParams::Desktop) - Reapply(); -} - -void RenderWidget::ScreenMetricsEmulator::OnShowContextMenu( - ContextMenuParams* params) { - params->x *= scale_; - params->x += offset_.x(); - params->y *= scale_; - params->y += offset_.y(); -} - -gfx::Rect RenderWidget::ScreenMetricsEmulator::AdjustValidationMessageAnchor( - const gfx::Rect& anchor) { - gfx::Rect scaled = gfx::ScaleToEnclosedRect(anchor, scale_); - scaled.set_x(scaled.x() + offset_.x()); - scaled.set_y(scaled.y() + offset_.y()); - return scaled; -} - // RenderWidget --------------------------------------------------------------- RenderWidget::RenderWidget(CompositorDependencies* compositor_deps, @@ -446,6 +220,7 @@ RenderWidget::RenderWidget(CompositorDependencies* compositor_deps, : routing_id_(MSG_ROUTING_NONE), compositor_deps_(compositor_deps), webwidget_(nullptr), + owner_delegate_(nullptr), opener_id_(MSG_ROUTING_NONE), top_controls_shrink_blink_size_(false), top_controls_height_(0.f), @@ -530,8 +305,9 @@ RenderWidget* RenderWidget::CreateForFrame( // routing ID. https://crbug.com/545684 RenderViewImpl* view = RenderViewImpl::FromRoutingID(routing_id); if (view) { - view->AttachWebFrameWidget(RenderWidget::CreateWebFrameWidget(view, frame)); - return view; + view->AttachWebFrameWidget( + RenderWidget::CreateWebFrameWidget(view->GetWidget(), frame)); + return view->GetWidget(); } scoped_refptr widget( new RenderWidget(compositor_deps, blink::WebPopupTypeNone, screen_info, @@ -549,7 +325,7 @@ RenderWidget* RenderWidget::CreateForFrame( } // static -blink::WebWidget* RenderWidget::CreateWebFrameWidget( +blink::WebFrameWidget* RenderWidget::CreateWebFrameWidget( RenderWidget* render_widget, blink::WebLocalFrame* frame) { if (!frame->parent()) { @@ -648,7 +424,7 @@ void RenderWidget::WasSwappedOut() { } void RenderWidget::SetPopupOriginAdjustmentsForEmulation( - ScreenMetricsEmulator* emulator) { + RenderWidgetScreenMetricsEmulator* emulator) { popup_origin_scale_for_emulation_ = emulator->scale(); popup_view_origin_for_emulation_ = emulator->applied_widget_rect().origin(); popup_screen_origin_for_emulation_ = gfx::Point( @@ -664,16 +440,10 @@ gfx::Rect RenderWidget::AdjustValidationMessageAnchor(const gfx::Rect& anchor) { return anchor; } -void RenderWidget::SetScreenMetricsEmulationParameters( - bool enabled, - const blink::WebDeviceEmulationParams& params) { - // This is only supported in RenderView. - NOTREACHED(); -} - #if defined(OS_MACOSX) || defined(OS_ANDROID) void RenderWidget::SetExternalPopupOriginAdjustmentsForEmulation( - ExternalPopupMenu* popup, ScreenMetricsEmulator* emulator) { + ExternalPopupMenu* popup, + RenderWidgetScreenMetricsEmulator* emulator) { popup->SetOriginScaleAndOffsetForEmulation( emulator->scale(), emulator->offset()); } @@ -711,8 +481,12 @@ bool RenderWidget::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_Move_ACK, OnRequestMoveAck) IPC_MESSAGE_HANDLER(ViewMsg_UpdateScreenRects, OnUpdateScreenRects) IPC_MESSAGE_HANDLER(ViewMsg_SetSurfaceIdNamespace, OnSetSurfaceIdNamespace) + IPC_MESSAGE_HANDLER(ViewMsg_WaitForNextFrameForTests, + OnWaitNextFrameForTests) #if defined(OS_ANDROID) IPC_MESSAGE_HANDLER(InputMsg_ImeEventAck, OnImeEventAck) + IPC_MESSAGE_HANDLER(InputMsg_RequestTextInputStateUpdate, + OnRequestTextInputStateUpdate) IPC_MESSAGE_HANDLER(ViewMsg_ShowImeIfNeeded, OnShowImeIfNeeded) #endif IPC_MESSAGE_HANDLER(ViewMsg_HandleCompositorProto, OnHandleCompositorProto) @@ -738,97 +512,22 @@ bool RenderWidget::Send(IPC::Message* message) { return RenderThread::Get()->Send(message); } -void RenderWidget::Resize(const gfx::Size& new_size, - const gfx::Size& physical_backing_size, - bool top_controls_shrink_blink_size, - float top_controls_height, - const gfx::Size& visible_viewport_size, - const gfx::Rect& resizer_rect, - bool is_fullscreen_granted, - blink::WebDisplayMode display_mode, - const ResizeAck resize_ack) { - if (resizing_mode_selector_->NeverUsesSynchronousResize()) { - // A resize ack shouldn't be requested if we have not ACK'd the previous - // one. - DCHECK(resize_ack != SEND_RESIZE_ACK || !next_paint_is_resize_ack()); - DCHECK(resize_ack == SEND_RESIZE_ACK || resize_ack == NO_RESIZE_ACK); - } - - // Ignore this during shutdown. - if (!webwidget_) - return; - - if (compositor_) - compositor_->setViewportSize(physical_backing_size); - - bool resized = size_ != new_size || - physical_backing_size_ != physical_backing_size; - - size_ = new_size; - physical_backing_size_ = physical_backing_size; - - top_controls_shrink_blink_size_ = top_controls_shrink_blink_size; - top_controls_height_ = top_controls_height; - visible_viewport_size_ = visible_viewport_size; - resizer_rect_ = resizer_rect; - - // NOTE: We may have entered fullscreen mode without changing our size. - bool fullscreen_change = is_fullscreen_granted_ != is_fullscreen_granted; - is_fullscreen_granted_ = is_fullscreen_granted; - display_mode_ = display_mode; - - webwidget_->setTopControlsHeight(top_controls_height, - top_controls_shrink_blink_size_); - - if (resized) { - gfx::Size new_widget_size = - IsUseZoomForDSFEnabled() ? physical_backing_size_ : size_; - // When resizing, we want to wait to paint before ACK'ing the resize. This - // ensures that we only resize as fast as we can paint. We only need to - // send an ACK if we are resized to a non-empty rect. - webwidget_->resize(new_widget_size); - } - WebSize visual_viewport_size; - - if (IsUseZoomForDSFEnabled()) { - visual_viewport_size = - gfx::ScaleToCeiledSize(visible_viewport_size, device_scale_factor_); - } else { - visual_viewport_size = visible_viewport_size_; - } - - webwidget()->resizeVisualViewport(visual_viewport_size); - - if (new_size.IsEmpty() || physical_backing_size.IsEmpty()) { - // In this case there is no paint/composite and therefore no - // ViewHostMsg_UpdateRect to send the resize ack with. We'd need to send the - // ack through a fake ViewHostMsg_UpdateRect or a different message. - DCHECK_EQ(resize_ack, NO_RESIZE_ACK); - } - - // Send the Resize_ACK flag once we paint again if requested. - if (resize_ack == SEND_RESIZE_ACK) - set_next_paint_is_resize_ack(); - - if (fullscreen_change) - DidToggleFullscreen(); - - // If a resize ack is requested and it isn't set-up, then no more resizes will - // come in and in general things will go wrong. - DCHECK(resize_ack != SEND_RESIZE_ACK || next_paint_is_resize_ack()); -} - void RenderWidget::SetWindowRectSynchronously( const gfx::Rect& new_window_rect) { - Resize(new_window_rect.size(), - gfx::ScaleToCeiledSize(new_window_rect.size(), device_scale_factor_), - top_controls_shrink_blink_size_, - top_controls_height_, - new_window_rect.size(), - gfx::Rect(), - is_fullscreen_granted_, - display_mode_, - NO_RESIZE_ACK); + ResizeParams params; + params.screen_info = screen_info_; + params.new_size = new_window_rect.size(); + params.physical_backing_size = + gfx::ScaleToCeiledSize(new_window_rect.size(), device_scale_factor_); + params.top_controls_shrink_blink_size = top_controls_shrink_blink_size_; + params.top_controls_height = top_controls_height_; + params.visible_viewport_size = new_window_rect.size(); + params.resizer_rect = gfx::Rect(); + params.is_fullscreen_granted = is_fullscreen_granted_; + params.display_mode = display_mode_; + params.needs_resize_ack = false; + Resize(params); + view_screen_rect_ = new_window_rect; window_screen_rect_ = new_window_rect; if (!did_show_) @@ -869,40 +568,38 @@ void RenderWidget::OnClose() { Release(); } -void RenderWidget::OnResize(const ViewMsg_Resize_Params& params) { +void RenderWidget::OnResize(const ResizeParams& params) { if (resizing_mode_selector_->ShouldAbortOnResize(this, params)) return; if (screen_metrics_emulator_) { - screen_metrics_emulator_->OnResizeMessage(params); + screen_metrics_emulator_->OnResize(params); return; } - bool orientation_changed = - screen_info_.orientationAngle != params.screen_info.orientationAngle; - - screen_info_ = params.screen_info; - SetDeviceScaleFactor(screen_info_.deviceScaleFactor); - Resize(params.new_size, - params.physical_backing_size, - params.top_controls_shrink_blink_size, - params.top_controls_height, - params.visible_viewport_size, - params.resizer_rect, - params.is_fullscreen_granted, - params.display_mode, - params.needs_resize_ack ? SEND_RESIZE_ACK : NO_RESIZE_ACK); - - if (orientation_changed) - OnOrientationChange(); + Resize(params); } void RenderWidget::OnEnableDeviceEmulation( const blink::WebDeviceEmulationParams& params) { - if (!screen_metrics_emulator_) - screen_metrics_emulator_.reset(new ScreenMetricsEmulator(this, params)); - else + if (!screen_metrics_emulator_) { + ResizeParams resize_params; + resize_params.screen_info = screen_info_; + resize_params.new_size = size_; + resize_params.physical_backing_size = physical_backing_size_; + resize_params.visible_viewport_size = visible_viewport_size_; + resize_params.top_controls_shrink_blink_size = + top_controls_shrink_blink_size_; + resize_params.top_controls_height = top_controls_height_; + resize_params.resizer_rect = resizer_rect_; + resize_params.is_fullscreen_granted = is_fullscreen_granted_; + resize_params.display_mode = display_mode_; + screen_metrics_emulator_.reset(new RenderWidgetScreenMetricsEmulator( + this, params, resize_params, view_screen_rect_, window_screen_rect_)); + screen_metrics_emulator_->Apply(); + } else { screen_metrics_emulator_->ChangeEmulationParams(params); + } } void RenderWidget::OnDisableDeviceEmulation() { @@ -963,6 +660,47 @@ GURL RenderWidget::GetURLForGraphicsContext3D() { return GURL(); } +void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type) { + if (!input_event) + return; + input_handler_->HandleInputEvent(*input_event, latency_info, dispatch_type); +} + +void RenderWidget::OnCursorVisibilityChange(bool is_visible) { + if (webwidget_) + webwidget_->setCursorVisibilityState(is_visible); +} + +void RenderWidget::OnMouseCaptureLost() { + if (webwidget_) + webwidget_->mouseCaptureLost(); +} + +void RenderWidget::OnSetFocus(bool enable) { + if (webwidget_) + webwidget_->setFocus(enable); +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetCompositorDelegate + +void RenderWidget::ApplyViewportDeltas( + const gfx::Vector2dF& inner_delta, + const gfx::Vector2dF& outer_delta, + const gfx::Vector2dF& elastic_overscroll_delta, + float page_scale, + float top_controls_delta) { + webwidget_->applyViewportDeltas(inner_delta, outer_delta, + elastic_overscroll_delta, page_scale, + top_controls_delta); +} + +void RenderWidget::BeginMainFrame(double frame_time_sec) { + webwidget_->beginFrame(frame_time_sec); +} + scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { DCHECK(webwidget_); // For widgets that are never visible, we don't start the compositor, so we @@ -976,14 +714,15 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { use_software = true; #if defined(MOJO_SHELL_CLIENT) - if (MojoShellConnection::Get() && !use_software) { + if (MojoShellConnection::Get() && !use_software && + command_line.HasSwitch(switches::kUseMusInRenderer)) { RenderWidgetMusConnection* connection = RenderWidgetMusConnection::GetOrCreate(routing_id()); return connection->CreateOutputSurface(); } #endif - scoped_refptr gpu_channel_host; + scoped_refptr gpu_channel_host; if (!use_software) { CauseForGpuLaunch cause = CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE; @@ -999,9 +738,23 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { use_software = true; } +#if defined(ENABLE_VULKAN) + scoped_refptr vulkan_context_provider; +#endif scoped_refptr context_provider; scoped_refptr worker_context_provider; if (!use_software) { +#if defined(ENABLE_VULKAN) + vulkan_context_provider = cc::VulkanInProcessContextProvider::Create(); + if (vulkan_context_provider) { + uint32_t output_surface_id = next_output_surface_id_++; + return scoped_ptr(new DelegatedCompositorOutputSurface( + routing_id(), output_surface_id, context_provider, + worker_context_provider, vulkan_context_provider, + frame_swap_message_queue_)); + } +#endif + context_provider = ContextProviderCommandBuffer::Create( CreateGraphicsContext3D(gpu_channel_host.get()), RENDER_COMPOSITOR_CONTEXT); @@ -1016,14 +769,16 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { #if defined(OS_ANDROID) if (SynchronousCompositorFactory* factory = SynchronousCompositorFactory::GetInstance()) { + uint32_t output_surface_id = next_output_surface_id_++; return factory->CreateOutputSurface( - routing_id(), frame_swap_message_queue_, context_provider, - worker_context_provider); + routing_id(), output_surface_id, frame_swap_message_queue_, + context_provider, worker_context_provider); } else if (RenderThreadImpl::current()->sync_compositor_message_filter()) { + uint32_t output_surface_id = next_output_surface_id_++; return make_scoped_ptr(new SynchronousCompositorOutputSurface( context_provider, worker_context_provider, routing_id(), - content::RenderThreadImpl::current() - ->sync_compositor_message_filter(), + output_surface_id, content::RenderThreadImpl::current() + ->sync_compositor_message_filter(), frame_swap_message_queue_)); } #endif @@ -1038,7 +793,11 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { DCHECK(compositor_deps_->GetCompositorImplThreadTaskRunner()); return make_scoped_ptr(new DelegatedCompositorOutputSurface( routing_id(), output_surface_id, context_provider, - worker_context_provider, frame_swap_message_queue_)); + worker_context_provider, +#if defined(ENABLE_VULKAN) + vulkan_context_provider, +#endif + frame_swap_message_queue_)); } if (!context_provider.get()) { @@ -1047,6 +806,9 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { return make_scoped_ptr(new CompositorOutputSurface( routing_id(), output_surface_id, nullptr, nullptr, +#if defined(ENABLE_VULKAN) + nullptr, +#endif std::move(software_device), frame_swap_message_queue_, true)); } @@ -1055,16 +817,72 @@ scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) { worker_context_provider, frame_swap_message_queue_, cc::RGBA_8888)); } +scoped_ptr +RenderWidget::CreateExternalBeginFrameSource() { + return compositor_deps_->CreateExternalBeginFrameSource(routing_id_); +} + +void RenderWidget::DidCommitAndDrawCompositorFrame() { + // NOTE: Tests may break if this event is renamed or moved. See + // tab_capture_performancetest.cc. + TRACE_EVENT0("gpu", "RenderWidget::DidCommitAndDrawCompositorFrame"); + // Notify subclasses that we initiated the paint operation. + DidInitiatePaint(); +} + +void RenderWidget::DidCommitCompositorFrame() { + FOR_EACH_OBSERVER(RenderFrameImpl, render_frames_, + DidCommitCompositorFrame()); + FOR_EACH_OBSERVER(RenderFrameProxy, render_frame_proxies_, + DidCommitCompositorFrame()); +#if defined(VIDEO_HOLE) + FOR_EACH_OBSERVER(RenderFrameImpl, video_hole_frames_, + DidCommitCompositorFrame()); +#endif // defined(VIDEO_HOLE) + input_handler_->FlushPendingInputEventAck(); +} + +void RenderWidget::DidCompletePageScaleAnimation() {} + +void RenderWidget::DidCompleteSwapBuffers() { + TRACE_EVENT0("renderer", "RenderWidget::DidCompleteSwapBuffers"); + + // Notify subclasses threaded composited rendering was flushed to the screen. + DidFlushPaint(); + + if (!next_paint_flags_ && !need_update_rect_for_auto_resize_) { + return; + } + + ViewHostMsg_UpdateRect_Params params; + params.view_size = size_; + params.flags = next_paint_flags_; + + Send(new ViewHostMsg_UpdateRect(routing_id_, params)); + next_paint_flags_ = 0; + need_update_rect_for_auto_resize_ = false; +} + +bool RenderWidget::ForOOPIF() const { + // TODO(simonhong): Remove this when we enable BeginFrame scheduling for + // OOPIF(crbug.com/471411). + return for_oopif_; +} + +void RenderWidget::ForwardCompositorProto(const std::vector& proto) { + Send(new ViewHostMsg_ForwardCompositorProto(routing_id_, proto)); +} + +bool RenderWidget::IsClosing() const { + return host_closing_; +} + void RenderWidget::OnSwapBuffersAborted() { TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersAborted"); // Schedule another frame so the compositor learns about it. ScheduleComposite(); } -void RenderWidget::OnSwapBuffersPosted() { - TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersPosted"); -} - void RenderWidget::OnSwapBuffersComplete() { TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersComplete"); @@ -1072,34 +890,75 @@ void RenderWidget::OnSwapBuffersComplete() { DidFlushPaint(); } -void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, - const ui::LatencyInfo& latency_info) { - if (!input_event) - return; - input_handler_->HandleInputEvent(*input_event, latency_info); +void RenderWidget::OnSwapBuffersPosted() { + TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersPosted"); } -void RenderWidget::OnCursorVisibilityChange(bool is_visible) { - if (webwidget_) - webwidget_->setCursorVisibilityState(is_visible); +void RenderWidget::RecordFrameTimingEvents( + scoped_ptr composite_events, + scoped_ptr main_frame_events) { + for (const auto& composite_event : *composite_events) { + int64_t frameId = composite_event.first; + const std::vector& events = + composite_event.second; + std::vector webEvents; + for (size_t i = 0; i < events.size(); ++i) { + webEvents.push_back(blink::WebFrameTimingEvent( + events[i].frame_id, + (events[i].timestamp - base::TimeTicks()).InSecondsF())); + } + webwidget_->recordFrameTimingEvent(blink::WebWidget::CompositeEvent, + frameId, webEvents); + } + for (const auto& main_frame_event : *main_frame_events) { + int64_t frameId = main_frame_event.first; + const std::vector& events = + main_frame_event.second; + std::vector webEvents; + for (size_t i = 0; i < events.size(); ++i) { + webEvents.push_back(blink::WebFrameTimingEvent( + events[i].frame_id, + (events[i].timestamp - base::TimeTicks()).InSecondsF(), + (events[i].end_time - base::TimeTicks()).InSecondsF())); + } + webwidget_->recordFrameTimingEvent(blink::WebWidget::RenderEvent, frameId, + webEvents); + } } -void RenderWidget::OnMouseCaptureLost() { - if (webwidget_) - webwidget_->mouseCaptureLost(); +void RenderWidget::RequestScheduleAnimation() { + scheduleAnimation(); } -void RenderWidget::OnSetFocus(bool enable) { - if (webwidget_) - webwidget_->setFocus(enable); +void RenderWidget::UpdateVisualState() { + webwidget_->updateAllLifecyclePhases(); +} + +void RenderWidget::WillBeginCompositorFrame() { + TRACE_EVENT0("gpu", "RenderWidget::willBeginCompositorFrame"); + + // The UpdateTextInputState can result in further layout and possibly + // enable GPU acceleration so they need to be called before any painting + // is done. + UpdateTextInputState(ShowIme::HIDE_IME, ChangeSource::FROM_NON_IME); + UpdateSelectionBounds(); + + FOR_EACH_OBSERVER(RenderFrameProxy, render_frame_proxies_, + WillBeginCompositorFrame()); } /////////////////////////////////////////////////////////////////////////////// // RenderWidgetInputHandlerDelegate -void RenderWidget::FocusChangeComplete() {} +void RenderWidget::FocusChangeComplete() { + if (owner_delegate_) + owner_delegate_->RenderWidgetFocusChangeComplete(); +} bool RenderWidget::HasTouchEventHandlersAt(const gfx::Point& point) const { + if (owner_delegate_) + return owner_delegate_->DoesRenderWidgetHaveTouchEventHandlersAt(point); + return true; } @@ -1124,7 +983,31 @@ void RenderWidget::ObserveWheelEventAndResult( } } -void RenderWidget::OnDidHandleKeyEvent() {} +void RenderWidget::ObserveGestureEventAndResult( + const blink::WebGestureEvent& gesture_event, + const gfx::Vector2dF& unused_delta, + bool event_processed) { + if (!compositor_deps_->IsElasticOverscrollEnabled()) + return; + + cc::InputHandlerScrollResult scroll_result; + scroll_result.did_scroll = event_processed; + scroll_result.did_overscroll_root = !unused_delta.IsZero(); + scroll_result.unused_scroll_delta = unused_delta; + + RenderThreadImpl* render_thread = RenderThreadImpl::current(); + InputHandlerManager* input_handler_manager = + render_thread ? render_thread->input_handler_manager() : NULL; + if (input_handler_manager) { + input_handler_manager->ObserveGestureEventAndResultOnMainThread( + routing_id_, gesture_event, scroll_result); + } +} + +void RenderWidget::OnDidHandleKeyEvent() { + if (owner_delegate_) + owner_delegate_->RenderWidgetDidHandleKeyEvent(); +} void RenderWidget::OnDidOverscroll(const DidOverscrollParams& params) { Send(new InputHostMsg_DidOverscroll(routing_id_, params)); @@ -1134,6 +1017,17 @@ void RenderWidget::OnInputEventAck(scoped_ptr input_event_ack) { Send(new InputHostMsg_HandleInputEvent_ACK(routing_id_, *input_event_ack)); } +void RenderWidget::NotifyInputEventHandled( + blink::WebInputEvent::Type handled_type) { + RenderThreadImpl* render_thread = RenderThreadImpl::current(); + InputHandlerManager* input_handler_manager = + render_thread ? render_thread->input_handler_manager() : NULL; + if (input_handler_manager) { + input_handler_manager->NotifyInputEventHandledOnMainThread(routing_id_, + handled_type); + } +} + void RenderWidget::SetInputHandler(RenderWidgetInputHandler* input_handler) { // Nothing to do here. RenderWidget created the |input_handler| and will take // ownership of it. We just verify here that we don't already have an input @@ -1167,6 +1061,7 @@ void RenderWidget::UpdateTextInputState(ShowIme show_ime, // Only sends text input params if they are changed or if the ime should be // shown. if (show_ime == ShowIme::IF_NEEDED || + (IsUsingImeThread() && change_source == ChangeSource::FROM_IME) || (text_input_type_ != new_type || text_input_mode_ != new_mode || text_input_info_ != new_info || can_compose_inline_ != new_can_compose_inline) @@ -1206,13 +1101,127 @@ void RenderWidget::UpdateTextInputState(ShowIme show_ime, } bool RenderWidget::WillHandleGestureEvent(const blink::WebGestureEvent& event) { + if (owner_delegate_) + return owner_delegate_->RenderWidgetWillHandleGestureEvent(event); + return false; } bool RenderWidget::WillHandleMouseEvent(const blink::WebMouseEvent& event) { + if (owner_delegate_) + return owner_delegate_->RenderWidgetWillHandleMouseEvent(event); + return false; } +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetScreenMetricsDelegate + +void RenderWidget::Redraw() { + set_next_paint_is_resize_ack(); + if (compositor_) + compositor_->SetNeedsRedrawRect(gfx::Rect(size_)); +} + +void RenderWidget::Resize(const ResizeParams& params) { + bool orientation_changed = + screen_info_.orientationAngle != params.screen_info.orientationAngle || + screen_info_.orientationType != params.screen_info.orientationType; + + screen_info_ = params.screen_info; + SetDeviceScaleFactor(screen_info_.deviceScaleFactor); + + if (resizing_mode_selector_->NeverUsesSynchronousResize()) { + // A resize ack shouldn't be requested if we have not ACK'd the previous + // one. + DCHECK(!params.needs_resize_ack || !next_paint_is_resize_ack()); + } + + // Ignore this during shutdown. + if (!webwidget_) + return; + + if (compositor_) + compositor_->setViewportSize(params.physical_backing_size); + + bool resized = size_ != params.new_size || + physical_backing_size_ != params.physical_backing_size; + + size_ = params.new_size; + physical_backing_size_ = params.physical_backing_size; + + top_controls_shrink_blink_size_ = params.top_controls_shrink_blink_size; + top_controls_height_ = params.top_controls_height; + visible_viewport_size_ = params.visible_viewport_size; + resizer_rect_ = params.resizer_rect; + + // NOTE: We may have entered fullscreen mode without changing our size. + bool fullscreen_change = + is_fullscreen_granted_ != params.is_fullscreen_granted; + is_fullscreen_granted_ = params.is_fullscreen_granted; + display_mode_ = params.display_mode; + + webwidget_->setTopControlsHeight(params.top_controls_height, + top_controls_shrink_blink_size_); + + if (resized) { + gfx::Size new_widget_size = size_; + if (IsUseZoomForDSFEnabled()) { + new_widget_size = gfx::ScaleToCeiledSize(new_widget_size, + GetOriginalDeviceScaleFactor()); + } + // When resizing, we want to wait to paint before ACK'ing the resize. This + // ensures that we only resize as fast as we can paint. We only need to + // send an ACK if we are resized to a non-empty rect. + webwidget_->resize(new_widget_size); + } + WebSize visual_viewport_size; + + if (IsUseZoomForDSFEnabled()) { + visual_viewport_size = gfx::ScaleToCeiledSize( + params.visible_viewport_size, + GetOriginalDeviceScaleFactor()); + } else { + visual_viewport_size = visible_viewport_size_; + } + + webwidget()->resizeVisualViewport(visual_viewport_size); + + if (params.new_size.IsEmpty() || params.physical_backing_size.IsEmpty()) { + // In this case there is no paint/composite and therefore no + // ViewHostMsg_UpdateRect to send the resize ack with. We'd need to send the + // ack through a fake ViewHostMsg_UpdateRect or a different message. + DCHECK(!params.needs_resize_ack); + } + + // Send the Resize_ACK flag once we paint again if requested. + if (params.needs_resize_ack) + set_next_paint_is_resize_ack(); + + if (fullscreen_change) + DidToggleFullscreen(); + + if (orientation_changed) + OnOrientationChange(); + + // If a resize ack is requested and it isn't set-up, then no more resizes will + // come in and in general things will go wrong. + DCHECK(!params.needs_resize_ack || next_paint_is_resize_ack()); +} + +void RenderWidget::SetScreenMetricsEmulationParameters( + bool enabled, + const blink::WebDeviceEmulationParams& params) { + // This is only supported in RenderView. + NOTREACHED(); +} + +void RenderWidget::SetScreenRects(const gfx::Rect& view_screen_rect, + const gfx::Rect& window_screen_rect) { + view_screen_rect_ = view_screen_rect; + window_screen_rect_ = window_screen_rect; +} + /////////////////////////////////////////////////////////////////////////////// // WebWidgetClient @@ -1248,7 +1257,8 @@ void RenderWidget::AutoResizeCompositor() { void RenderWidget::initializeLayerTreeView() { DCHECK(!host_closing_); - compositor_ = RenderWidgetCompositor::Create(this, compositor_deps_); + compositor_ = RenderWidgetCompositor::Create(this, device_scale_factor_, + compositor_deps_); compositor_->setViewportSize(physical_backing_size_); OnDeviceScaleFactorChanged(); // For background pages and certain tests, we don't want to trigger @@ -1286,58 +1296,6 @@ void RenderWidget::didMeaningfulLayout(blink::WebMeaningfulLayout layout_type) { DidMeaningfulLayout(layout_type)); } -void RenderWidget::WillBeginCompositorFrame() { - TRACE_EVENT0("gpu", "RenderWidget::willBeginCompositorFrame"); - - // The UpdateTextInputState can result in further layout and possibly - // enable GPU acceleration so they need to be called before any painting - // is done. - UpdateTextInputState(ShowIme::HIDE_IME, ChangeSource::FROM_NON_IME); - UpdateSelectionBounds(); -} - -void RenderWidget::DidCommitCompositorFrame() { - FOR_EACH_OBSERVER(RenderFrameImpl, render_frames_, - DidCommitCompositorFrame()); - FOR_EACH_OBSERVER(RenderFrameProxy, render_frame_proxies_, - DidCommitCompositorFrame()); -#if defined(VIDEO_HOLE) - FOR_EACH_OBSERVER(RenderFrameImpl, video_hole_frames_, - DidCommitCompositorFrame()); -#endif // defined(VIDEO_HOLE) - input_handler_->FlushPendingInputEventAck(); -} - -void RenderWidget::DidCommitAndDrawCompositorFrame() { - // NOTE: Tests may break if this event is renamed or moved. See - // tab_capture_performancetest.cc. - TRACE_EVENT0("gpu", "RenderWidget::DidCommitAndDrawCompositorFrame"); - // Notify subclasses that we initiated the paint operation. - DidInitiatePaint(); -} - -void RenderWidget::DidCompleteSwapBuffers() { - TRACE_EVENT0("renderer", "RenderWidget::DidCompleteSwapBuffers"); - - // Notify subclasses threaded composited rendering was flushed to the screen. - DidFlushPaint(); - - if (!next_paint_flags_ && - !need_update_rect_for_auto_resize_ && - !plugin_window_moves_.size()) { - return; - } - - ViewHostMsg_UpdateRect_Params params; - params.view_size = size_; - params.plugin_window_moves.swap(plugin_window_moves_); - params.flags = next_paint_flags_; - - Send(new ViewHostMsg_UpdateRect(routing_id_, params)); - next_paint_flags_ = 0; - need_update_rect_for_auto_resize_ = false; -} - void RenderWidget::ScheduleComposite() { if (compositor_ && compositor_deps_->GetCompositorImplThreadTaskRunner().get()) { @@ -1553,6 +1511,7 @@ WebRect RenderWidget::windowResizerRect() { void RenderWidget::OnImeSetComposition( const base::string16& text, const std::vector& underlines, + const gfx::Range& replacement_range, int selection_start, int selection_end) { if (!ShouldHandleImeEvent()) return; @@ -1588,9 +1547,8 @@ void RenderWidget::OnImeConfirmComposition(const base::string16& text, void RenderWidget::OnDeviceScaleFactorChanged() { if (!compositor_) return; - if (IsUseZoomForDSFEnabled()) - compositor_->SetPaintedDeviceScaleFactor(device_scale_factor_); + compositor_->SetPaintedDeviceScaleFactor(GetOriginalDeviceScaleFactor()); else compositor_->setDeviceScaleFactor(device_scale_factor_); } @@ -1627,15 +1585,23 @@ void RenderWidget::OnSetTextDirection(WebTextDirection direction) { void RenderWidget::OnUpdateScreenRects(const gfx::Rect& view_screen_rect, const gfx::Rect& window_screen_rect) { if (screen_metrics_emulator_) { - screen_metrics_emulator_->OnUpdateScreenRectsMessage( - view_screen_rect, window_screen_rect); + screen_metrics_emulator_->OnUpdateScreenRects(view_screen_rect, + window_screen_rect); } else { - view_screen_rect_ = view_screen_rect; - window_screen_rect_ = window_screen_rect; + SetScreenRects(view_screen_rect, window_screen_rect); } Send(new ViewHostMsg_UpdateScreenRects_ACK(routing_id())); } +void RenderWidget::OnUpdateWindowScreenRect( + const gfx::Rect& window_screen_rect) { + if (screen_metrics_emulator_) { + screen_metrics_emulator_->OnUpdateWindowScreenRect(window_screen_rect); + } else { + window_screen_rect_ = window_screen_rect; + } +} + void RenderWidget::OnSetSurfaceIdNamespace(uint32_t surface_id_namespace) { if (compositor_) compositor_->SetSurfaceIdNamespace(surface_id_namespace); @@ -1657,10 +1623,6 @@ ui::TextInputType RenderWidget::GetTextInputType() { } void RenderWidget::UpdateCompositionInfo(bool should_update_range) { -#if defined(OS_ANDROID) -// TODO(yukawa): Start sending character bounds when the browser side -// implementation becomes ready (crbug.com/424866). -#else TRACE_EVENT0("renderer", "RenderWidget::UpdateCompositionInfo"); gfx::Range range = gfx::Range(); if (should_update_range) { @@ -1677,12 +1639,11 @@ void RenderWidget::UpdateCompositionInfo(bool should_update_range) { composition_range_ = range; Send(new InputHostMsg_ImeCompositionRangeChanged( routing_id(), composition_range_, composition_character_bounds_)); -#endif } void RenderWidget::convertViewportToWindow(blink::WebRect* rect) { if (IsUseZoomForDSFEnabled()) { - float reverse = 1 / device_scale_factor_; + float reverse = 1 / GetOriginalDeviceScaleFactor(); // TODO(oshima): We may need to allow pixel precision here as the the // anchor element can be placed at half pixel. gfx::Rect window_rect = @@ -1694,6 +1655,15 @@ void RenderWidget::convertViewportToWindow(blink::WebRect* rect) { } } +void RenderWidget::convertWindowToViewport(blink::WebFloatRect* rect) { + if (IsUseZoomForDSFEnabled()) { + rect->x *= GetOriginalDeviceScaleFactor(); + rect->y *= GetOriginalDeviceScaleFactor(); + rect->width *= GetOriginalDeviceScaleFactor(); + rect->height *= GetOriginalDeviceScaleFactor(); + } +} + void RenderWidget::OnShowImeIfNeeded() { #if defined(OS_ANDROID) || defined(USE_AURA) UpdateTextInputState(ShowIme::IF_NEEDED, ChangeSource::FROM_NON_IME); @@ -1715,12 +1685,20 @@ void RenderWidget::OnImeEventAck() { DCHECK_GE(text_input_info_history_.size(), 1u); text_input_info_history_.pop_front(); } + +void RenderWidget::OnRequestTextInputStateUpdate() { + DCHECK(!ime_event_guard_); + UpdateSelectionBounds(); + UpdateTextInputState(ShowIme::HIDE_IME, ChangeSource::FROM_IME); +} #endif bool RenderWidget::ShouldHandleImeEvent() { #if defined(OS_ANDROID) if (!webwidget_) return false; + if (IsUsingImeThread()) + return true; // We cannot handle IME events if there is any chance that the event we are // receiving here from the browser is based on the state that is different @@ -1755,21 +1733,24 @@ bool RenderWidget::SetDeviceColorProfile( return false; device_color_profile_ = color_profile; + + if (owner_delegate_) + owner_delegate_->RenderWidgetDidSetColorProfile(color_profile); + return true; } -void RenderWidget::ResetDeviceColorProfileForTesting() { - if (!device_color_profile_.empty()) - device_color_profile_.clear(); - device_color_profile_.push_back('0'); +void RenderWidget::OnOrientationChange() { } -void RenderWidget::OnOrientationChange() { +void RenderWidget::DidInitiatePaint() { + if (owner_delegate_) + owner_delegate_->RenderWidgetDidCommitAndDrawCompositorFrame(); } -gfx::Vector2d RenderWidget::GetScrollOffset() { - // Bare RenderWidgets don't support scroll offset. - return gfx::Vector2d(); +void RenderWidget::DidFlushPaint() { + if (owner_delegate_) + owner_delegate_->RenderWidgetDidFlushPaint(); } void RenderWidget::SetHidden(bool hidden) { @@ -1813,6 +1794,14 @@ void RenderWidget::set_next_paint_is_repaint_ack() { next_paint_flags_ |= ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK; } +bool RenderWidget::IsUsingImeThread() { +#if defined(OS_ANDROID) + return base::FeatureList::IsEnabled(features::kImeThread); +#else + return false; +#endif +} + void RenderWidget::OnImeEventGuardStart(ImeEventGuard* guard) { if (!ime_event_guard_) ime_event_guard_ = guard; @@ -1823,7 +1812,7 @@ void RenderWidget::OnImeEventGuardFinish(ImeEventGuard* guard) { #if defined(OS_ANDROID) // In case a from-IME event (e.g. touch) ends up in not-from-IME event // (e.g. long press gesture), we want to treat it as not-from-IME event - // so that AdapterInputConnection can make changes to its Editable model. + // so that ReplicaInputConnection can make changes to its Editable model. // Therefore, we want to mark this text state update as 'from IME' only // when all the nested events are all originating from IME. ime_event_guard_->set_from_ime( @@ -1890,33 +1879,42 @@ void RenderWidget::UpdateSelectionBounds() { UpdateCompositionInfo(false); } -void RenderWidget::ForwardCompositorProto(const std::vector& proto) { - Send(new ViewHostMsg_ForwardCompositorProto(routing_id_, proto)); +void RenderWidget::SetDeviceColorProfileForTesting( + const std::vector& color_profile) { + SetDeviceColorProfile(color_profile); +} + +void RenderWidget::ResetDeviceColorProfileForTesting() { + std::vector color_profile; + color_profile.push_back('0'); + SetDeviceColorProfile(color_profile); } // Check blink::WebTextInputType and ui::TextInputType is kept in sync. -#define STATIC_ASSERT_WTIT_ENUM_MATCH(a, b) \ - static_assert(int(blink::WebTextInputType##a) \ - == int(ui::TEXT_INPUT_TYPE_##b), \ - "mismatching enums: " #a) - -STATIC_ASSERT_WTIT_ENUM_MATCH(None, NONE); -STATIC_ASSERT_WTIT_ENUM_MATCH(Text, TEXT); -STATIC_ASSERT_WTIT_ENUM_MATCH(Password, PASSWORD); -STATIC_ASSERT_WTIT_ENUM_MATCH(Search, SEARCH); -STATIC_ASSERT_WTIT_ENUM_MATCH(Email, EMAIL); -STATIC_ASSERT_WTIT_ENUM_MATCH(Number, NUMBER); -STATIC_ASSERT_WTIT_ENUM_MATCH(Telephone, TELEPHONE); -STATIC_ASSERT_WTIT_ENUM_MATCH(URL, URL); -STATIC_ASSERT_WTIT_ENUM_MATCH(Date, DATE); -STATIC_ASSERT_WTIT_ENUM_MATCH(DateTime, DATE_TIME); -STATIC_ASSERT_WTIT_ENUM_MATCH(DateTimeLocal, DATE_TIME_LOCAL); -STATIC_ASSERT_WTIT_ENUM_MATCH(Month, MONTH); -STATIC_ASSERT_WTIT_ENUM_MATCH(Time, TIME); -STATIC_ASSERT_WTIT_ENUM_MATCH(Week, WEEK); -STATIC_ASSERT_WTIT_ENUM_MATCH(TextArea, TEXT_AREA); -STATIC_ASSERT_WTIT_ENUM_MATCH(ContentEditable, CONTENT_EDITABLE); -STATIC_ASSERT_WTIT_ENUM_MATCH(DateTimeField, DATE_TIME_FIELD); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeNone, ui::TEXT_INPUT_TYPE_NONE); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeText, ui::TEXT_INPUT_TYPE_TEXT); +STATIC_ASSERT_ENUM(blink::WebTextInputTypePassword, + ui::TEXT_INPUT_TYPE_PASSWORD); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeSearch, ui::TEXT_INPUT_TYPE_SEARCH); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeEmail, ui::TEXT_INPUT_TYPE_EMAIL); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeNumber, ui::TEXT_INPUT_TYPE_NUMBER); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeTelephone, + ui::TEXT_INPUT_TYPE_TELEPHONE); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeURL, ui::TEXT_INPUT_TYPE_URL); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeDate, ui::TEXT_INPUT_TYPE_DATE); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeDateTime, + ui::TEXT_INPUT_TYPE_DATE_TIME); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeDateTimeLocal, + ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeMonth, ui::TEXT_INPUT_TYPE_MONTH); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeTime, ui::TEXT_INPUT_TYPE_TIME); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeWeek, ui::TEXT_INPUT_TYPE_WEEK); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeTextArea, + ui::TEXT_INPUT_TYPE_TEXT_AREA); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeContentEditable, + ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE); +STATIC_ASSERT_ENUM(blink::WebTextInputTypeDateTimeField, + ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD); ui::TextInputType RenderWidget::WebKitToUiTextInputType( blink::WebTextInputType type) { @@ -2020,6 +2018,13 @@ void RenderWidget::didOverscroll( const blink::WebFloatSize& accumulatedRootOverScroll, const blink::WebFloatPoint& position, const blink::WebFloatSize& velocity) { +#if defined(OS_MACOSX) + // On OSX the user can disable the elastic overscroll effect. If that's the + // case, don't forward the overscroll notification. + DCHECK(compositor_deps()); + if (!compositor_deps()->IsElasticOverscrollEnabled()) + return; +#endif input_handler_->DidOverscrollFromBlink(unusedDelta, accumulatedRootOverScroll, position, velocity); } @@ -2029,34 +2034,6 @@ void RenderWidget::StartCompositor() { compositor_->setVisible(true); } -void RenderWidget::SchedulePluginMove(const WebPluginGeometry& move) { - size_t i = 0; - for (; i < plugin_window_moves_.size(); ++i) { - if (plugin_window_moves_[i].window == move.window) { - if (move.rects_valid) { - plugin_window_moves_[i] = move; - } else { - plugin_window_moves_[i].visible = move.visible; - } - break; - } - } - - if (i == plugin_window_moves_.size()) - plugin_window_moves_.push_back(move); -} - -void RenderWidget::CleanupWindowInPluginMoves(gfx::PluginWindowHandle window) { - for (WebPluginGeometryVector::iterator i = plugin_window_moves_.begin(); - i != plugin_window_moves_.end(); ++i) { - if (i->window == window) { - plugin_window_moves_.erase(i); - break; - } - } -} - - RenderWidgetCompositor* RenderWidget::compositor() const { return compositor_.get(); } @@ -2079,10 +2056,21 @@ void RenderWidget::hasTouchEventHandlers(bool has_handlers) { Send(new ViewHostMsg_HasTouchEventHandlers(routing_id_, has_handlers)); } -// Check blink::WebTouchAction and blink::WebTouchActionAuto is kept in sync -#define STATIC_ASSERT_WTI_ENUM_MATCH(a, b) \ - static_assert(int(blink::WebTouchAction##a) == int(TOUCH_ACTION_##b), \ - "mismatching enums: " #a) +// Check blink::WebTouchAction and content::TouchAction is kept in sync. +STATIC_ASSERT_ENUM(blink::WebTouchActionNone, TOUCH_ACTION_NONE); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanLeft, TOUCH_ACTION_PAN_LEFT); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanRight, TOUCH_ACTION_PAN_RIGHT); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanX, TOUCH_ACTION_PAN_X); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanUp, TOUCH_ACTION_PAN_UP); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanDown, TOUCH_ACTION_PAN_DOWN); +STATIC_ASSERT_ENUM(blink::WebTouchActionPanY, TOUCH_ACTION_PAN_Y); +STATIC_ASSERT_ENUM(blink::WebTouchActionPan, TOUCH_ACTION_PAN); +STATIC_ASSERT_ENUM(blink::WebTouchActionPinchZoom, TOUCH_ACTION_PINCH_ZOOM); +STATIC_ASSERT_ENUM(blink::WebTouchActionManipulation, + TOUCH_ACTION_MANIPULATION); +STATIC_ASSERT_ENUM(blink::WebTouchActionDoubleTapZoom, + TOUCH_ACTION_DOUBLE_TAP_ZOOM); +STATIC_ASSERT_ENUM(blink::WebTouchActionAuto, TOUCH_ACTION_AUTO); void RenderWidget::setTouchAction( blink::WebTouchAction web_touch_action) { @@ -2092,20 +2080,6 @@ void RenderWidget::setTouchAction( if (input_handler_->handling_event_type() != WebInputEvent::TouchStart) return; - // Verify the same values are used by the types so we can cast between them. - STATIC_ASSERT_WTI_ENUM_MATCH(None, NONE); - STATIC_ASSERT_WTI_ENUM_MATCH(PanLeft, PAN_LEFT); - STATIC_ASSERT_WTI_ENUM_MATCH(PanRight, PAN_RIGHT); - STATIC_ASSERT_WTI_ENUM_MATCH(PanX, PAN_X); - STATIC_ASSERT_WTI_ENUM_MATCH(PanUp, PAN_UP); - STATIC_ASSERT_WTI_ENUM_MATCH(PanDown, PAN_DOWN); - STATIC_ASSERT_WTI_ENUM_MATCH(PanY, PAN_Y); - STATIC_ASSERT_WTI_ENUM_MATCH(Pan, PAN); - STATIC_ASSERT_WTI_ENUM_MATCH(PinchZoom, PINCH_ZOOM); - STATIC_ASSERT_WTI_ENUM_MATCH(Manipulation, MANIPULATION); - STATIC_ASSERT_WTI_ENUM_MATCH(DoubleTapZoom, DOUBLE_TAP_ZOOM); - STATIC_ASSERT_WTI_ENUM_MATCH(Auto, AUTO); - content::TouchAction content_touch_action = static_cast(web_touch_action); Send(new InputHostMsg_SetTouchAction(routing_id_, content_touch_action)); @@ -2113,29 +2087,24 @@ void RenderWidget::setTouchAction( void RenderWidget::didUpdateTextOfFocusedElementByNonUserInput() { #if defined(OS_ANDROID) - text_field_is_dirty_ = true; + if (!IsUsingImeThread()) + text_field_is_dirty_ = true; #endif } scoped_ptr -RenderWidget::CreateGraphicsContext3D(GpuChannelHost* gpu_channel_host) { - // Explicitly disable antialiasing for the compositor. As of the time of - // this writing, the only platform that supported antialiasing for the - // compositor was Mac OS X, because the on-screen OpenGL context creation - // code paths on Windows and Linux didn't yet have multisampling support. - // Mac OS X essentially always behaves as though it's rendering offscreen. - // Multisampling has a heavy cost especially on devices with relatively low - // fill rate like most notebooks, and the Mac implementation would need to - // be optimized to resolve directly into the IOSurface shared between the - // GPU and browser processes. For these reasons and to avoid platform - // disparities we explicitly disable antialiasing. - blink::WebGraphicsContext3D::Attributes attributes; - attributes.antialias = false; - attributes.shareResources = true; - attributes.noAutomaticFlushes = true; - attributes.depth = false; - attributes.stencil = false; - bool lose_context_when_out_of_memory = true; +RenderWidget::CreateGraphicsContext3D(gpu::GpuChannelHost* gpu_channel_host) { + // This is for an offscreen context for raster in the compositor. So the + // default framebuffer doesn't need alpha, depth, stencil, antialiasing. + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 0; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.bind_generates_resource = false; + attributes.lose_context_when_out_of_memory = true; + WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits limits; #if defined(OS_ANDROID) bool using_synchronous_compositing = @@ -2170,9 +2139,13 @@ RenderWidget::CreateGraphicsContext3D(GpuChannelHost* gpu_channel_host) { limits.start_transfer_buffer_size = 64 * 1024; limits.min_transfer_buffer_size = 64 * 1024; + bool share_resources = true; + bool automatic_flushes = false; + return make_scoped_ptr(new WebGraphicsContext3DCommandBufferImpl( - 0, GetURLForGraphicsContext3D(), gpu_channel_host, attributes, - lose_context_when_out_of_memory, limits, NULL)); + gpu::kNullSurfaceHandle, GetURLForGraphicsContext3D(), gpu_channel_host, + attributes, gfx::PreferIntegratedGpu, share_resources, automatic_flushes, + limits, nullptr)); } void RenderWidget::RegisterRenderFrameProxy(RenderFrameProxy* proxy) { @@ -2201,4 +2174,16 @@ void RenderWidget::UnregisterVideoHoleFrame(RenderFrameImpl* frame) { } #endif // defined(VIDEO_HOLE) +void RenderWidget::OnWaitNextFrameForTests(int routing_id) { + QueueMessage(new ViewHostMsg_WaitForNextFrameForTests_ACK(routing_id), + MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE); +} + +float RenderWidget::GetOriginalDeviceScaleFactor() const { + return + screen_metrics_emulator_ ? + screen_metrics_emulator_->original_screen_info().deviceScaleFactor : + device_scale_factor_; +} + } // namespace content diff --git a/chromium/content/renderer/render_widget.h b/chromium/content/renderer/render_widget.h index 20d98e87de2..d8851906157 100644 --- a/chromium/content/renderer/render_widget.h +++ b/chromium/content/renderer/render_widget.h @@ -23,6 +23,8 @@ #include "content/common/cursors/webcursor.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "content/common/input/synthetic_gesture_params.h" +#include "content/renderer/devtools/render_widget_screen_metrics_emulator_delegate.h" +#include "content/renderer/gpu/render_widget_compositor_delegate.h" #include "content/renderer/input/render_widget_input_handler.h" #include "content/renderer/input/render_widget_input_handler_delegate.h" #include "content/renderer/message_delivery_policy.h" @@ -43,14 +45,11 @@ #include "ui/base/ime/text_input_type.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/geometry/vector2d_f.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/range/range.h" #include "ui/surface/transport_dib.h" -struct ViewMsg_Resize_Params; - namespace IPC { class SyncMessage; class SyncMessageFilter; @@ -58,6 +57,7 @@ class SyncMessageFilter; namespace blink { struct WebDeviceEmulationParams; +class WebFrameWidget; class WebGestureEvent; class WebLocalFrame; class WebMouseEvent; @@ -86,10 +86,12 @@ class ImeEventGuard; class RenderFrameImpl; class RenderFrameProxy; class RenderWidgetCompositor; +class RenderWidgetOwnerDelegate; +class RenderWidgetScreenMetricsEmulator; class ResizingModeSelector; struct ContextMenuParams; struct DidOverscrollParams; -struct WebPluginGeometry; +struct ResizeParams; // RenderWidget provides a communication bridge between a WebWidget and // a RenderWidgetHost, the latter of which lives in a different process. @@ -103,7 +105,9 @@ class CONTENT_EXPORT RenderWidget : public IPC::Listener, public IPC::Sender, NON_EXPORTED_BASE(virtual public blink::WebWidgetClient), + public RenderWidgetCompositorDelegate, public RenderWidgetInputHandlerDelegate, + public RenderWidgetScreenMetricsEmulatorDelegate, public base::RefCounted { public: // Creates a new RenderWidget. The opener_id is the routing ID of the @@ -130,14 +134,13 @@ class CONTENT_EXPORT RenderWidget CompositorDependencies* compositor_deps() const { return compositor_deps_; } blink::WebWidget* webwidget() const { return webwidget_; } - gfx::Size size() const { return size_; } + const gfx::Size& size() const { return size_; } bool is_fullscreen_granted() const { return is_fullscreen_granted_; } blink::WebDisplayMode display_mode() const { return display_mode_; } bool is_hidden() const { return is_hidden_; } // Temporary for debugging purposes... bool closing() const { return closing_; } bool is_swapped_out() { return is_swapped_out_; } - bool for_oopif() { return for_oopif_; } bool has_host_context_menu_location() { return has_host_context_menu_location_; } @@ -145,6 +148,13 @@ class CONTENT_EXPORT RenderWidget return host_context_menu_location_; } + void set_owner_delegate(RenderWidgetOwnerDelegate* owner_delegate) { + DCHECK(!owner_delegate_); + owner_delegate_ = owner_delegate; + } + + RenderWidgetOwnerDelegate* owner_delegate() { return owner_delegate_; } + // ScreenInfo exposed so it can be passed to subframe RenderWidgets. blink::WebScreenInfo screen_info() const { return screen_info_; } @@ -168,21 +178,63 @@ class CONTENT_EXPORT RenderWidget // IPC::Sender bool Send(IPC::Message* msg) override; + // RenderWidgetCompositorDelegate + void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta, + const gfx::Vector2dF& outer_delta, + const gfx::Vector2dF& elastic_overscroll_delta, + float page_scale, + float top_controls_delta) override; + void BeginMainFrame(double frame_time_sec) override; + scoped_ptr CreateOutputSurface(bool fallback) override; + scoped_ptr CreateExternalBeginFrameSource() override; + void DidCommitAndDrawCompositorFrame() override; + void DidCommitCompositorFrame() override; + void DidCompletePageScaleAnimation() override; + void DidCompleteSwapBuffers() override; + bool ForOOPIF() const override; + void ForwardCompositorProto(const std::vector& proto) override; + bool IsClosing() const override; + void OnSwapBuffersAborted() override; + void OnSwapBuffersComplete() override; + void OnSwapBuffersPosted() override; + void RecordFrameTimingEvents( + scoped_ptr composite_events, + scoped_ptr main_frame_events) + override; + void RequestScheduleAnimation() override; + void UpdateVisualState() override; + void WillBeginCompositorFrame() override; + // RenderWidgetInputHandlerDelegate void FocusChangeComplete() override; bool HasTouchEventHandlersAt(const gfx::Point& point) const override; void ObserveWheelEventAndResult(const blink::WebMouseWheelEvent& wheel_event, const gfx::Vector2dF& wheel_unused_delta, bool event_processed) override; + void ObserveGestureEventAndResult(const blink::WebGestureEvent& gesture_event, + const gfx::Vector2dF& unused_delta, + bool event_processed) override; + void OnDidHandleKeyEvent() override; void OnDidOverscroll(const DidOverscrollParams& params) override; void OnInputEventAck(scoped_ptr input_event_ack) override; + void NotifyInputEventHandled( + blink::WebInputEvent::Type handled_type) override; void SetInputHandler(RenderWidgetInputHandler* input_handler) override; void UpdateTextInputState(ShowIme show_ime, ChangeSource change_source) override; bool WillHandleGestureEvent(const blink::WebGestureEvent& event) override; bool WillHandleMouseEvent(const blink::WebMouseEvent& event) override; + // RenderWidgetScreenMetricsDelegate + void Redraw() override; + void Resize(const ResizeParams& resize_params) override; + void SetScreenMetricsEmulationParameters( + bool enabled, + const blink::WebDeviceEmulationParams& params) override; + void SetScreenRects(const gfx::Rect& view_screen_rect, + const gfx::Rect& window_screen_rect) override; + // blink::WebWidgetClient void didAutoResize(const blink::WebSize& new_size) override; void initializeLayerTreeView() override; @@ -208,6 +260,7 @@ class CONTENT_EXPORT RenderWidget const blink::WebFloatSize& velocity) override; void showImeIfNeeded() override; void convertViewportToWindow(blink::WebRect* rect) override; + void convertWindowToViewport(blink::WebFloatRect* rect) override; // Override point to obtain that the current input method state and caret // position. @@ -231,14 +284,6 @@ class CONTENT_EXPORT RenderWidget // Stop compositing. void WillCloseLayerTreeView(); - // Called when a plugin is moved. These events are queued up and sent with - // the next paint or scroll message to the host. - void SchedulePluginMove(const WebPluginGeometry& move); - - // Called when a plugin window has been destroyed, to make sure the currently - // pending moves don't try to reference it. - void CleanupWindowInPluginMoves(gfx::PluginWindowHandle window); - RenderWidgetCompositor* compositor() const; const RenderWidgetInputHandler& input_handler() const { @@ -256,8 +301,6 @@ class CONTENT_EXPORT RenderWidget // we should not send an extra ack (see SendAckForMouseMoveFromDebugger). void IgnoreAckForMouseMoveFromDebugger(); - virtual scoped_ptr CreateOutputSurface(bool fallback); - // Callback for use with synthetic gestures (e.g. BeginSmoothScroll). typedef base::Callback SyntheticGestureCompletionCallback; @@ -279,6 +322,9 @@ class CONTENT_EXPORT RenderWidget // |policy| see the comment on MessageDeliveryPolicy. void QueueMessage(IPC::Message* msg, MessageDeliveryPolicy policy); + // Check whether IME thread is being used or not. + bool IsUsingImeThread(); + // Handle start and finish of IME event guard. void OnImeEventGuardStart(ImeEventGuard* guard); void OnImeEventGuardFinish(ImeEventGuard* guard); @@ -286,49 +332,21 @@ class CONTENT_EXPORT RenderWidget // Returns whether we currently should handle an IME event. bool ShouldHandleImeEvent(); - // Called by the compositor when page scale animation completed. - virtual void DidCompletePageScaleAnimation() {} - - // ScreenMetricsEmulator class manages screen emulation inside a render - // widget. This includes resizing, placing view on the screen at desired - // position, changing device scale factor, and scaling down the whole - // widget if required to fit into the browser window. - class ScreenMetricsEmulator; + void SetPopupOriginAdjustmentsForEmulation( + RenderWidgetScreenMetricsEmulator* emulator); - void SetPopupOriginAdjustmentsForEmulation(ScreenMetricsEmulator* emulator); gfx::Rect AdjustValidationMessageAnchor(const gfx::Rect& anchor); - // Indicates that the compositor is about to begin a frame. This is primarily - // to signal to flow control mechanisms that a frame is beginning, not to - // perform actual painting work. - void WillBeginCompositorFrame(); - - // Notifies about a compositor frame commit operation having finished. - virtual void DidCommitCompositorFrame(); - - // Notifies that the draw commands for a committed frame have been issued. - void DidCommitAndDrawCompositorFrame(); - - // Notifies that the compositor has posted a swapbuffers operation to the GPU - // process. - void DidCompleteSwapBuffers(); void ScheduleComposite(); void ScheduleCompositeWithForcedRedraw(); - // Called by the compositor in single-threaded mode when a swap is posted, - // completes or is aborted. - void OnSwapBuffersPosted(); - void OnSwapBuffersComplete(); - void OnSwapBuffersAborted(); - // Checks if the selection bounds have been changed. If they are changed, // the new value will be sent to the browser process. void UpdateSelectionBounds(); // Called by the compositor to forward a proto that represents serialized // compositor state. - void ForwardCompositorProto(const std::vector& proto); virtual void GetSelectionBounds(gfx::Rect* start, gfx::Rect* end); @@ -340,7 +358,9 @@ class CONTENT_EXPORT RenderWidget // handle composition range and composition character bounds. void UpdateCompositionInfo(bool should_update_range); - bool host_closing() const { return host_closing_; } + // Change the device ICC color profile while running a layout test. + void SetDeviceColorProfileForTesting(const std::vector& color_profile); + void ResetDeviceColorProfileForTesting(); protected: // Friend RefCounted so that the dtor can be non-public. Using this class @@ -364,8 +384,9 @@ class CONTENT_EXPORT RenderWidget ~RenderWidget() override; - static blink::WebWidget* CreateWebFrameWidget(RenderWidget* render_widget, - blink::WebLocalFrame* frame); + static blink::WebFrameWidget* CreateWebFrameWidget( + RenderWidget* render_widget, + blink::WebLocalFrame* frame); // Creates a WebWidget based on the popup type. static blink::WebWidget* CreateWebWidget(RenderWidget* render_widget); @@ -394,35 +415,24 @@ class CONTENT_EXPORT RenderWidget // Close the underlying WebWidget. virtual void Close(); - // Resizes the render widget. - void Resize(const gfx::Size& new_size, - const gfx::Size& physical_backing_size, - bool top_controls_shrink_blink_size, - float top_controls_height, - const gfx::Size& visible_viewport_size, - const gfx::Rect& resizer_rect, - bool is_fullscreen_granted, - blink::WebDisplayMode display_mode, - ResizeAck resize_ack); // Used to force the size of a window when running layout tests. void SetWindowRectSynchronously(const gfx::Rect& new_window_rect); - virtual void SetScreenMetricsEmulationParameters( - bool enabled, - const blink::WebDeviceEmulationParams& params); #if defined(OS_MACOSX) || defined(OS_ANDROID) void SetExternalPopupOriginAdjustmentsForEmulation( - ExternalPopupMenu* popup, ScreenMetricsEmulator* emulator); + ExternalPopupMenu* popup, + RenderWidgetScreenMetricsEmulator* emulator); #endif // RenderWidget IPC message handlers void OnHandleInputEvent(const blink::WebInputEvent* event, - const ui::LatencyInfo& latency_info); + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type); void OnCursorVisibilityChange(bool is_visible); void OnMouseCaptureLost(); virtual void OnSetFocus(bool enable); void OnClose(); void OnCreatingNewAck(); - virtual void OnResize(const ViewMsg_Resize_Params& params); + virtual void OnResize(const ResizeParams& params); void OnEnableDeviceEmulation(const blink::WebDeviceEmulationParams& params); void OnDisableDeviceEmulation(); void OnColorProfile(const std::vector& color_profile); @@ -436,6 +446,7 @@ class CONTENT_EXPORT RenderWidget virtual void OnImeSetComposition( const base::string16& text, const std::vector& underlines, + const gfx::Range& replacement_range, int selection_start, int selection_end); virtual void OnImeConfirmComposition(const base::string16& text, @@ -451,6 +462,7 @@ class CONTENT_EXPORT RenderWidget void OnGetFPS(); void OnUpdateScreenRects(const gfx::Rect& view_screen_rect, const gfx::Rect& window_screen_rect); + void OnUpdateWindowScreenRect(const gfx::Rect& window_screen_rect); void OnShowImeIfNeeded(); void OnSetSurfaceIdNamespace(uint32_t surface_id_namespace); void OnHandleCompositorProto(const std::vector& proto); @@ -461,6 +473,9 @@ class CONTENT_EXPORT RenderWidget // Called by the browser process for every required IME acknowledgement. void OnImeEventAck(); + + // Called by the browser process to update text input state. + void OnRequestTextInputStateUpdate(); #endif // Notify the compositor about a change in viewport size. This should be @@ -469,8 +484,7 @@ class CONTENT_EXPORT RenderWidget void AutoResizeCompositor(); virtual void SetDeviceScaleFactor(float device_scale_factor); - virtual bool SetDeviceColorProfile(const std::vector& color_profile); - virtual void ResetDeviceColorProfileForTesting(); + bool SetDeviceColorProfile(const std::vector& color_profile); virtual void OnOrientationChange(); @@ -480,15 +494,11 @@ class CONTENT_EXPORT RenderWidget // the ACK that the screen has been updated. For a given paint operation, // these overrides will always be called in the order DidInitiatePaint, // DidFlushPaint. - virtual void DidInitiatePaint() {} - virtual void DidFlushPaint() {} + virtual void DidInitiatePaint(); + virtual void DidFlushPaint(); virtual GURL GetURLForGraphicsContext3D(); - // Gets the scroll offset of this widget, if this widget has a notion of - // scroll offset. - virtual gfx::Vector2d GetScrollOffset(); - // Sets the "hidden" state of this widget. All accesses to is_hidden_ should // use this method so that we can properly inform the RenderThread of our // state. @@ -550,7 +560,10 @@ class CONTENT_EXPORT RenderWidget // Creates a 3D context associated with this view. scoped_ptr CreateGraphicsContext3D( - GpuChannelHost* gpu_channel_host); + gpu::GpuChannelHost* gpu_channel_host); + + // Sends an ACK to the browser process during the next compositor frame. + void OnWaitNextFrameForTests(int routing_id); // Routing ID that allows us to communicate to the parent browser process // RenderWidgetHost. When MSG_ROUTING_NONE, no messages may be sent. @@ -564,6 +577,9 @@ class CONTENT_EXPORT RenderWidget // May be NULL when the window is closing. blink::WebWidget* webwidget_; + // The delegate of the owner of this object. + RenderWidgetOwnerDelegate* owner_delegate_; + // This is lazily constructed and must not outlive webwidget_. scoped_ptr compositor_; @@ -678,10 +694,6 @@ class CONTENT_EXPORT RenderWidget // The kind of popup this widget represents, NONE if not a popup. blink::WebPopupType popup_type_; - // Holds all the needed plugin window moves for a scroll. - typedef std::vector WebPluginGeometryVector; - WebPluginGeometryVector plugin_window_moves_; - // While we are waiting for the browser to update window sizes, we track the // pending size temporarily. int pending_window_rect_count_; @@ -726,7 +738,7 @@ class CONTENT_EXPORT RenderWidget std::deque text_input_info_history_; #endif - scoped_ptr screen_metrics_emulator_; + scoped_ptr screen_metrics_emulator_; // Popups may be displaced when screen metrics emulation is enabled. // These values are used to properly adjust popup position. @@ -755,6 +767,10 @@ class CONTENT_EXPORT RenderWidget scoped_ptr render_widget_scheduling_state_; + private: + // When emulated, this returns original device scale factor. + float GetOriginalDeviceScaleFactor() const; + DISALLOW_COPY_AND_ASSIGN(RenderWidget); }; diff --git a/chromium/content/renderer/render_widget_browsertest.cc b/chromium/content/renderer/render_widget_browsertest.cc index e3caf1f71ec..489536df3df 100644 --- a/chromium/content/renderer/render_widget_browsertest.cc +++ b/chromium/content/renderer/render_widget_browsertest.cc @@ -2,16 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/common/view_messages.h" -#include "content/public/test/render_widget_test.h" +#include "content/common/resize_params.h" +#include "content/public/test/render_view_test.h" +#include "content/renderer/render_view_impl.h" #include "content/renderer/render_widget.h" namespace content { +class RenderWidgetTest : public RenderViewTest { + protected: + RenderWidget* widget() { return view_->GetWidget(); } + + void OnResize(const ResizeParams& params) { + widget()->OnResize(params); + } + + bool next_paint_is_resize_ack() { + return widget()->next_paint_is_resize_ack(); + } +}; + TEST_F(RenderWidgetTest, OnResize) { // The initial bounds is empty, so setting it to the same thing should do // nothing. - ViewMsg_Resize_Params resize_params; + ResizeParams resize_params; resize_params.screen_info = blink::WebScreenInfo(); resize_params.new_size = gfx::Size(); resize_params.physical_backing_size = gfx::Size(); @@ -68,9 +82,8 @@ class RenderWidgetInitialSizeTest : public RenderWidgetTest { : RenderWidgetTest(), initial_size_(200, 100) {} protected: - scoped_ptr InitialSizeParams() override { - scoped_ptr initial_size_params( - new ViewMsg_Resize_Params()); + scoped_ptr InitialSizeParams() override { + scoped_ptr initial_size_params(new ResizeParams()); initial_size_params->new_size = initial_size_; initial_size_params->physical_backing_size = initial_size_; initial_size_params->needs_resize_ack = true; diff --git a/chromium/content/renderer/render_widget_fullscreen_pepper.cc b/chromium/content/renderer/render_widget_fullscreen_pepper.cc index 9f9ebe05797..cbd9f948152 100644 --- a/chromium/content/renderer/render_widget_fullscreen_pepper.cc +++ b/chromium/content/renderer/render_widget_fullscreen_pepper.cc @@ -11,12 +11,12 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "build/build_config.h" -#include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/view_messages.h" #include "content/public/common/content_switches.h" #include "content/renderer/gpu/render_widget_compositor.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/render_thread_impl.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/platform/WebCanvas.h" #include "third_party/WebKit/public/platform/WebCursorInfo.h" @@ -43,7 +43,6 @@ using blink::WebTextDirection; using blink::WebTextInputType; using blink::WebVector; using blink::WebWidget; -using blink::WGC3Dintptr; namespace content { @@ -315,7 +314,7 @@ void RenderWidgetFullscreenPepper::Destroy() { Release(); } -void RenderWidgetFullscreenPepper::DidChangeCursor( +void RenderWidgetFullscreenPepper::PepperDidChangeCursor( const blink::WebCursorInfo& cursor) { didChangeCursor(cursor); } @@ -370,8 +369,7 @@ void RenderWidgetFullscreenPepper::Close() { RenderWidget::Close(); } -void RenderWidgetFullscreenPepper::OnResize( - const ViewMsg_Resize_Params& params) { +void RenderWidgetFullscreenPepper::OnResize(const ResizeParams& params) { if (layer_) layer_->setBounds(blink::WebSize(params.new_size)); RenderWidget::OnResize(params); @@ -385,11 +383,9 @@ GURL RenderWidgetFullscreenPepper::GetURLForGraphicsContext3D() { return active_url_; } -void RenderWidgetFullscreenPepper::SetDeviceScaleFactor( - float device_scale_factor) { - RenderWidget::SetDeviceScaleFactor(device_scale_factor); +void RenderWidgetFullscreenPepper::OnDeviceScaleFactorChanged() { if (compositor_) - compositor_->setDeviceScaleFactor(device_scale_factor); + compositor_->setDeviceScaleFactor(device_scale_factor_); } } // namespace content diff --git a/chromium/content/renderer/render_widget_fullscreen_pepper.h b/chromium/content/renderer/render_widget_fullscreen_pepper.h index 4c144c81872..7f4bd4487ad 100644 --- a/chromium/content/renderer/render_widget_fullscreen_pepper.h +++ b/chromium/content/renderer/render_widget_fullscreen_pepper.h @@ -39,7 +39,7 @@ class RenderWidgetFullscreenPepper : public RenderWidgetFullscreen, void InvalidateRect(const blink::WebRect& rect) override; void ScrollRect(int dx, int dy, const blink::WebRect& rect) override; void Destroy() override; - void DidChangeCursor(const blink::WebCursorInfo& cursor) override; + void PepperDidChangeCursor(const blink::WebCursorInfo& cursor) override; void SetLayer(blink::WebLayer* layer) override; // IPC::Listener implementation. This overrides the implementation @@ -64,14 +64,14 @@ class RenderWidgetFullscreenPepper : public RenderWidgetFullscreen, void DidInitiatePaint() override; void DidFlushPaint() override; void Close() override; - void OnResize(const ViewMsg_Resize_Params& params) override; + void OnResize(const ResizeParams& params) override; // RenderWidgetFullscreen API. blink::WebWidget* CreateWebWidget() override; // RenderWidget overrides. GURL GetURLForGraphicsContext3D() override; - void SetDeviceScaleFactor(float device_scale_factor) override; + void OnDeviceScaleFactorChanged() override; private: // URL that is responsible for this widget, passed to ggl::CreateViewContext. diff --git a/chromium/content/renderer/render_widget_owner_delegate.h b/chromium/content/renderer/render_widget_owner_delegate.h new file mode 100644 index 00000000000..4c76ddd19bd --- /dev/null +++ b/chromium/content/renderer/render_widget_owner_delegate.h @@ -0,0 +1,58 @@ +// 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 CONTENT_RENDERER_RENDER_WIDGET_OWNER_DELEGATE_H_ +#define CONTENT_RENDERER_RENDER_WIDGET_OWNER_DELEGATE_H_ + +#include "content/common/content_export.h" + +namespace blink { +class WebGestureEvent; +class WebMouseEvent; +} + +namespace gfx { +class Point; +} + +namespace content { + +// +// RenderWidgetOwnerDelegate +// +// An interface implemented by an object owning a RenderWidget. This is +// intended to be temporary until the RenderViewImpl and RenderWidget classes +// are disentangled; see http://crbug.com/583347 and http://crbug.com/478281. +class CONTENT_EXPORT RenderWidgetOwnerDelegate { + public: + // The RenderWidget set a color profile. + virtual void RenderWidgetDidSetColorProfile( + const std::vector& color_profile) = 0; + + // As in RenderWidgetInputHandlerDelegate. + virtual void RenderWidgetFocusChangeComplete() = 0; + virtual bool DoesRenderWidgetHaveTouchEventHandlersAt( + const gfx::Point& point) const = 0; + virtual void RenderWidgetDidHandleKeyEvent() = 0; + virtual bool RenderWidgetWillHandleGestureEvent( + const blink::WebGestureEvent& event) = 0; + virtual bool RenderWidgetWillHandleMouseEvent( + const blink::WebMouseEvent& event) = 0; + + // Painting notifications. RenderWidgetDidCommitAndDrawCompositorFrame happens + // when that has completed, and subsequent rendering won't affect the painted + // content. DidFlushPaint happens once we've received the ACK that the screen + // has been updated. For a given paint operation, these overrides will always + // be called in the order RenderWidgetDidCommitAndDrawCompositorFrame, + // RenderWidgetDidFlushPaint. + virtual void RenderWidgetDidCommitAndDrawCompositorFrame() = 0; + virtual void RenderWidgetDidFlushPaint() = 0; + + protected: + virtual ~RenderWidgetOwnerDelegate() {} +}; + +} // namespace content + +#endif // CONTENT_RENDERER_RENDER_WIDGET_OWNER_DELEGATE_H_ diff --git a/chromium/content/renderer/render_widget_unittest.cc b/chromium/content/renderer/render_widget_unittest.cc index cdd195a5a2b..05ebc45a747 100644 --- a/chromium/content/renderer/render_widget_unittest.cc +++ b/chromium/content/renderer/render_widget_unittest.cc @@ -7,18 +7,45 @@ #include #include "base/macros.h" +#include "base/test/histogram_tester.h" #include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/common/input/web_input_event_traits.h" #include "content/common/input_messages.h" #include "content/public/test/mock_render_thread.h" #include "content/test/fake_compositor_dependencies.h" #include "content/test/mock_render_process.h" #include "ipc/ipc_test_sink.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/gfx/geometry/rect.h" +using testing::_; + namespace content { +namespace { + +const char* EVENT_LISTENER_RESULT_HISTOGRAM = "Event.PassiveListeners"; + +enum { + PASSIVE_LISTENER_UMA_ENUM_PASSIVE, + PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE, + PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED, + PASSIVE_LISTENER_UMA_ENUM_CANCELABLE, + PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED, + PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING, + PASSIVE_LISTENER_UMA_ENUM_COUNT +}; + +class MockWebWidget : public blink::WebWidget { + public: + MOCK_METHOD1(handleInputEvent, + blink::WebInputEventResult(const blink::WebInputEvent&)); +}; + +} // namespace + class InteractiveRenderWidget : public RenderWidget { public: explicit InteractiveRenderWidget(CompositorDependencies* compositor_deps) @@ -29,6 +56,7 @@ class InteractiveRenderWidget : public RenderWidget { false, false), always_overscroll_(false) { + webwidget_ = &mock_webwidget_; // A RenderWidget is not fully initialized until it has a routing ID. SetRoutingID(++next_routing_id_); } @@ -38,7 +66,11 @@ class InteractiveRenderWidget : public RenderWidget { } void SendInputEvent(const blink::WebInputEvent& event) { - OnHandleInputEvent(&event, ui::LatencyInfo()); + OnHandleInputEvent( + &event, ui::LatencyInfo(), + WebInputEventTraits::ShouldBlockEventStream(event) + ? InputEventDispatchType::DISPATCH_TYPE_BLOCKING + : InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING); } void set_always_overscroll(bool overscroll) { @@ -47,8 +79,10 @@ class InteractiveRenderWidget : public RenderWidget { IPC::TestSink* sink() { return &sink_; } + MockWebWidget* mock_webwidget() { return &mock_webwidget_; } + protected: - ~InteractiveRenderWidget() override {} + ~InteractiveRenderWidget() override { webwidget_ = nullptr; } // Overridden from RenderWidget: bool HasTouchEventHandlersAt(const gfx::Point& point) const override { @@ -86,6 +120,7 @@ class InteractiveRenderWidget : public RenderWidget { std::vector rects_; IPC::TestSink sink_; bool always_overscroll_; + MockWebWidget mock_webwidget_; static int next_routing_id_; DISALLOW_COPY_AND_ASSIGN(InteractiveRenderWidget); @@ -101,11 +136,16 @@ class RenderWidgetUnittest : public testing::Test { InteractiveRenderWidget* widget() const { return widget_.get(); } + const base::HistogramTester& histogram_tester() const { + return histogram_tester_; + } + private: MockRenderProcess render_process_; MockRenderThread render_thread_; FakeCompositorDependencies compositor_deps_; scoped_refptr widget_; + base::HistogramTester histogram_tester_; DISALLOW_COPY_AND_ASSIGN(RenderWidgetUnittest); }; @@ -114,6 +154,10 @@ TEST_F(RenderWidgetUnittest, TouchHitTestSinglePoint) { SyntheticWebTouchEvent touch; touch.PressPoint(10, 10); + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillRepeatedly( + ::testing::Return(blink::WebInputEventResult::NotHandled)); + widget()->SendInputEvent(touch); ASSERT_EQ(1u, widget()->sink()->message_count()); @@ -132,6 +176,10 @@ TEST_F(RenderWidgetUnittest, TouchHitTestSinglePoint) { rects.push_back(gfx::Rect(25, 0, 10, 10)); widget()->SetTouchRegion(rects); + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillRepeatedly( + ::testing::Return(blink::WebInputEventResult::NotHandled)); + widget()->SendInputEvent(touch); ASSERT_EQ(1u, widget()->sink()->message_count()); message = widget()->sink()->GetMessageAt(0); @@ -151,6 +199,10 @@ TEST_F(RenderWidgetUnittest, TouchHitTestMultiplePoints) { SyntheticWebTouchEvent touch; touch.PressPoint(25, 25); + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillRepeatedly( + ::testing::Return(blink::WebInputEventResult::NotHandled)); + widget()->SendInputEvent(touch); ASSERT_EQ(1u, widget()->sink()->message_count()); @@ -179,6 +231,10 @@ TEST_F(RenderWidgetUnittest, TouchHitTestMultiplePoints) { TEST_F(RenderWidgetUnittest, EventOverscroll) { widget()->set_always_overscroll(true); + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillRepeatedly( + ::testing::Return(blink::WebInputEventResult::NotHandled)); + blink::WebGestureEvent scroll; scroll.type = blink::WebInputEvent::GestureScrollUpdate; scroll.x = -10; @@ -221,4 +277,54 @@ TEST_F(RenderWidgetUnittest, FlingOverscroll) { widget()->sink()->ClearMessages(); } +TEST_F(RenderWidgetUnittest, RenderWidgetInputEventUmaMetrics) { + SyntheticWebTouchEvent touch; + touch.PressPoint(10, 10); + + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .Times(4) + .WillRepeatedly( + ::testing::Return(blink::WebInputEventResult::NotHandled)); + + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_CANCELABLE, 1); + + touch.dispatchType = blink::WebInputEvent::DispatchType::EventNonBlocking; + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE, + 1); + + touch.dispatchType = + blink::WebInputEvent::DispatchType::ListenersNonBlockingPassive; + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_PASSIVE, 1); + + touch.dispatchType = + blink::WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive; + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount( + EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING, 1); + + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillOnce( + ::testing::Return(blink::WebInputEventResult::HandledSuppressed)); + touch.dispatchType = blink::WebInputEvent::DispatchType::Blocking; + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount(EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED, 1); + + EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_)) + .WillOnce( + ::testing::Return(blink::WebInputEventResult::HandledApplication)); + touch.dispatchType = blink::WebInputEvent::DispatchType::Blocking; + widget()->SendInputEvent(touch); + histogram_tester().ExpectBucketCount( + EVENT_LISTENER_RESULT_HISTOGRAM, + PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED, 1); +} + } // namespace content diff --git a/chromium/content/renderer/renderer.sb b/chromium/content/renderer/renderer.sb index 7e07985a2f1..10b6b75419b 100644 --- a/chromium/content/renderer/renderer.sb +++ b/chromium/content/renderer/renderer.sb @@ -34,9 +34,13 @@ (allow file-read-metadata (regex #"^/(private/)?etc$")) ; https://crbug.com/508935 +; TODO(tkent): Remove this section when we drop 'results' attribute support. +; https://crbug.com/590117 (if (param-true? elcap-or-later) (allow file-read* + (literal "/usr/lib/libcrypto.0.9.8.dylib") (literal "/usr/lib/libcsfde.dylib") (literal "/usr/lib/libcurl.4.dylib") (literal "/usr/lib/libCoreStorage.dylib") + (literal "/usr/lib/libsasl2.2.dylib") (literal "/usr/lib/libutil.dylib"))) diff --git a/chromium/content/renderer/renderer_blink_platform_impl.cc b/chromium/content/renderer/renderer_blink_platform_impl.cc index c4d4fed5409..45da15f57e8 100644 --- a/chromium/content/renderer/renderer_blink_platform_impl.cc +++ b/chromium/content/renderer/renderer_blink_platform_impl.cc @@ -29,10 +29,10 @@ #include "content/child/file_info_util.h" #include "content/child/fileapi/webfilesystem_impl.h" #include "content/child/indexed_db/webidbfactory_impl.h" -#include "content/child/npapi/npobject_util.h" #include "content/child/quota_dispatcher.h" #include "content/child/quota_message_filter.h" #include "content/child/simple_webmimeregistry_impl.h" +#include "content/child/storage_util.h" #include "content/child/thread_safe_sender.h" #include "content/child/web_database_observer_impl.h" #include "content/child/web_url_loader_impl.h" @@ -42,29 +42,32 @@ #include "content/common/file_utilities_messages.h" #include "content/common/frame_messages.h" #include "content/common/gpu/client/context_provider_command_buffer.h" -#include "content/common/gpu/client/gpu_channel_host.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" -#include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/common/gpu_process_launch_causes.h" #include "content/common/mime_registry_messages.h" #include "content/common/render_process_messages.h" #include "content/public/common/content_switches.h" #include "content/public/common/service_registry.h" #include "content/public/common/webplugininfo.h" #include "content/public/renderer/content_renderer_client.h" -#include "content/renderer/battery_status/battery_status_dispatcher.h" +#include "content/public/renderer/media_stream_utils.h" #include "content/renderer/cache_storage/webserviceworkercachestorage_impl.h" #include "content/renderer/device_sensors/device_light_event_pump.h" #include "content/renderer/device_sensors/device_motion_event_pump.h" #include "content/renderer/device_sensors/device_orientation_absolute_event_pump.h" #include "content/renderer/device_sensors/device_orientation_event_pump.h" +#include "content/renderer/dom_storage/local_storage_cached_areas.h" +#include "content/renderer/dom_storage/local_storage_namespace.h" #include "content/renderer/dom_storage/webstoragenamespace_impl.h" #include "content/renderer/gamepad_shared_memory_reader.h" #include "content/renderer/media/audio_decoder.h" #include "content/renderer/media/canvas_capture_handler.h" +#include "content/renderer/media/html_video_element_capturer_source.h" #include "content/renderer/media/media_recorder_handler.h" #include "content/renderer/media/renderer_webaudiodevice_impl.h" #include "content/renderer/media/renderer_webmidiaccessor_impl.h" #include "content/renderer/media/rtc_certificate_generator.h" +#include "content/renderer/mojo/blink_service_registry_impl.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/renderer_clipboard_delegate.h" #include "content/renderer/screen_orientation/screen_orientation_observer.h" @@ -72,16 +75,18 @@ #include "content/renderer/webgraphicscontext3d_provider_impl.h" #include "content/renderer/webpublicsuffixlist_impl.h" #include "gpu/config/gpu_info.h" +#include "gpu/ipc/client/gpu_channel_host.h" #include "ipc/ipc_sync_message_filter.h" #include "media/audio/audio_output_device.h" #include "media/base/audio_hardware_config.h" -#include "media/base/key_systems.h" #include "media/base/mime_util.h" #include "media/blink/webcontentdecryptionmodule_impl.h" #include "media/filters/stream_parser_factory.h" #include "storage/common/database/database_identifier.h" #include "storage/common/quota/quota_types.h" -#include "third_party/WebKit/public/platform/WebBatteryStatusListener.h" +#include "third_party/WebKit/public/platform/BlameContext.h" +#include "third_party/WebKit/public/platform/FilePathConversion.h" +#include "third_party/WebKit/public/platform/URLConversion.h" #include "third_party/WebKit/public/platform/WebBlobRegistry.h" #include "third_party/WebKit/public/platform/WebDeviceLightListener.h" #include "third_party/WebKit/public/platform/WebFileInfo.h" @@ -89,6 +94,7 @@ #include "third_party/WebKit/public/platform/WebMediaStreamCenter.h" #include "third_party/WebKit/public/platform/WebMediaStreamCenterClient.h" #include "third_party/WebKit/public/platform/WebPluginListBuilder.h" +#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebVector.h" #include "third_party/WebKit/public/platform/modules/device_orientation/WebDeviceMotionListener.h" @@ -148,7 +154,9 @@ using blink::WebGamepad; using blink::WebGamepads; using blink::WebIDBFactory; using blink::WebMIDIAccessor; +using blink::WebMediaPlayer; using blink::WebMediaRecorderHandler; +using blink::WebMediaStream; using blink::WebMediaStreamCenter; using blink::WebMediaStreamCenterClient; using blink::WebMediaStreamTrack; @@ -171,9 +179,6 @@ base::LazyInstance::Leaky g_test_device_motion_data = LAZY_INSTANCE_INITIALIZER; base::LazyInstance::Leaky g_test_device_orientation_data = LAZY_INSTANCE_INITIALIZER; -// Set in startListening() when running layout tests, unset in stopListening(), -// not owned by us. -blink::WebBatteryStatusListener* g_test_battery_status_listener = nullptr; } // namespace @@ -184,8 +189,7 @@ class RendererBlinkPlatformImpl::MimeRegistry public: blink::WebMimeRegistry::SupportsType supportsMediaMIMEType( const blink::WebString& mime_type, - const blink::WebString& codecs, - const blink::WebString& key_system) override; + const blink::WebString& codecs) override; bool supportsMediaSourceMIMEType(const blink::WebString& mime_type, const blink::WebString& codecs) override; blink::WebString mimeTypeForExtension( @@ -218,9 +222,9 @@ class RendererBlinkPlatformImpl::SandboxSupport blink::WebUChar32 character, const char* preferred_locale, blink::WebFallbackFont* fallbackFont) override; - void getRenderStyleForStrike(const char* family, - int sizeAndStyle, - blink::WebFontRenderStyle* out) override; + void getWebFontRenderStyleForStrike(const char* family, + int sizeAndStyle, + blink::WebFontRenderStyle* out) override; private: // WebKit likes to ask us for the correct font family to use for a set of @@ -235,7 +239,8 @@ class RendererBlinkPlatformImpl::SandboxSupport //------------------------------------------------------------------------------ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl( - scheduler::RendererScheduler* renderer_scheduler) + scheduler::RendererScheduler* renderer_scheduler, + base::WeakPtr service_registry) : BlinkPlatformImpl(renderer_scheduler->DefaultTaskRunner()), main_thread_(renderer_scheduler->CreateMainThread()), clipboard_delegate_(new RendererClipboardDelegate), @@ -246,7 +251,8 @@ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl( default_task_runner_(renderer_scheduler->DefaultTaskRunner()), loading_task_runner_(renderer_scheduler->LoadingTaskRunner()), web_scrollbar_behavior_(new WebScrollbarBehaviorImpl), - renderer_scheduler_(renderer_scheduler) { + renderer_scheduler_(renderer_scheduler), + blink_service_registry_(new BlinkServiceRegistryImpl(service_registry)) { #if !defined(OS_ANDROID) && !defined(OS_WIN) if (g_sandbox_enabled && sandboxEnabled()) { sandbox_support_.reset(new RendererBlinkPlatformImpl::SandboxSupport); @@ -260,15 +266,21 @@ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl( sync_message_filter_ = ChildThreadImpl::current()->sync_message_filter(); thread_safe_sender_ = ChildThreadImpl::current()->thread_safe_sender(); quota_message_filter_ = ChildThreadImpl::current()->quota_message_filter(); - blob_registry_.reset(new WebBlobRegistryImpl(thread_safe_sender_.get())); + blob_registry_.reset(new WebBlobRegistryImpl( + RenderThreadImpl::current()->GetIOMessageLoopProxy().get(), + base::ThreadTaskRunnerHandle::Get(), thread_safe_sender_.get())); web_idb_factory_.reset(new WebIDBFactoryImpl(thread_safe_sender_.get())); web_database_observer_impl_.reset( new WebDatabaseObserverImpl(sync_message_filter_.get())); } + + top_level_blame_context_.Initialize(); + renderer_scheduler_->SetTopLevelBlameContext(&top_level_blame_context_); } RendererBlinkPlatformImpl::~RendererBlinkPlatformImpl() { WebFileSystemImpl::DeleteThreadSpecificInstance(); + renderer_scheduler_->SetTopLevelBlameContext(nullptr); } void RendererBlinkPlatformImpl::Shutdown() { @@ -282,27 +294,13 @@ void RendererBlinkPlatformImpl::Shutdown() { //------------------------------------------------------------------------------ -double RendererBlinkPlatformImpl::currentTimeSeconds() { - return renderer_scheduler_->CurrentTimeSeconds(); -} - -double RendererBlinkPlatformImpl::monotonicallyIncreasingTimeSeconds() { - return renderer_scheduler_->MonotonicallyIncreasingTimeSeconds(); -} - -//------------------------------------------------------------------------------ - blink::WebURLLoader* RendererBlinkPlatformImpl::createURLLoader() { ChildThreadImpl* child_thread = ChildThreadImpl::current(); // There may be no child thread in RenderViewTests. These tests can still use // data URLs to bypass the ResourceDispatcher. - scoped_ptr task_runner( - new scheduler::WebTaskRunnerImpl( - loading_task_runner_->BelongsToCurrentThread() - ? loading_task_runner_ : base::ThreadTaskRunnerHandle::Get())); return new content::WebURLLoaderImpl( child_thread ? child_thread->resource_dispatcher() : NULL, - std::move(task_runner)); + make_scoped_ptr(currentThread()->getWebTaskRunner()->clone())); } blink::WebThread* RendererBlinkPlatformImpl::currentThread() { @@ -311,6 +309,10 @@ blink::WebThread* RendererBlinkPlatformImpl::currentThread() { return BlinkPlatformImpl::currentThread(); } +blink::BlameContext* RendererBlinkPlatformImpl::topLevelBlameContext() { + return &top_level_blame_context_; +} + blink::WebClipboard* RendererBlinkPlatformImpl::clipboard() { blink::WebClipboard* clipboard = GetContentClient()->renderer()->OverrideWebClipboard(); @@ -425,6 +427,15 @@ void RendererBlinkPlatformImpl::suddenTerminationChanged(bool enabled) { } WebStorageNamespace* RendererBlinkPlatformImpl::createLocalStorageNamespace() { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kMojoLocalStorage)) { + if (!local_storage_cached_areas_) { + local_storage_cached_areas_.reset(new LocalStorageCachedAreas( + RenderThreadImpl::current()->GetStoragePartitionService())); + } + return new LocalStorageNamespace(local_storage_cached_areas_.get()); + } + return new WebStorageNamespaceImpl(); } @@ -438,11 +449,9 @@ WebIDBFactory* RendererBlinkPlatformImpl::idbFactory() { //------------------------------------------------------------------------------ blink::WebServiceWorkerCacheStorage* RendererBlinkPlatformImpl::cacheStorage( - const WebString& origin_identifier) { - const GURL origin = - storage::GetOriginFromIdentifier(origin_identifier.utf8()); + const blink::WebSecurityOrigin& security_origin) { return new WebServiceWorkerCacheStorageImpl(thread_safe_sender_.get(), - origin); + security_origin); } //------------------------------------------------------------------------------ @@ -451,36 +460,20 @@ WebFileSystem* RendererBlinkPlatformImpl::fileSystem() { return WebFileSystemImpl::ThreadSpecificInstance(default_task_runner_); } +WebString RendererBlinkPlatformImpl::fileSystemCreateOriginIdentifier( + const blink::WebSecurityOrigin& origin) { + return WebString::fromUTF8(storage::GetIdentifierFromOrigin( + WebSecurityOriginToGURL(origin))); +} + //------------------------------------------------------------------------------ WebMimeRegistry::SupportsType RendererBlinkPlatformImpl::MimeRegistry::supportsMediaMIMEType( const WebString& mime_type, - const WebString& codecs, - const WebString& key_system) { + const WebString& codecs) { const std::string mime_type_ascii = ToASCIIOrEmpty(mime_type); - if (!key_system.isEmpty()) { - // Check whether the key system is supported with the mime_type and codecs. - - // Chromium only supports ASCII parameters. - if (!base::IsStringASCII(key_system)) - return IsNotSupported; - - std::string key_system_ascii = - media::GetUnprefixedKeySystemName(base::UTF16ToASCII( - base::StringPiece16(key_system))); - std::vector codec_vector; - media::ParseCodecString(ToASCIIOrEmpty(codecs), &codec_vector, true); - - if (!media::PrefixedIsSupportedKeySystemWithMediaMimeType( - mime_type_ascii, codec_vector, key_system_ascii)) { - return IsNotSupported; - } - - // Continue processing the mime_type and codecs. - } - std::vector codec_vector; media::ParseCodecString(ToASCIIOrEmpty(codecs), &codec_vector, false); return static_cast( @@ -501,15 +494,12 @@ bool RendererBlinkPlatformImpl::MimeRegistry::supportsMediaSourceMIMEType( WebString RendererBlinkPlatformImpl::MimeRegistry::mimeTypeForExtension( const WebString& file_extension) { - if (IsPluginProcess()) - return SimpleWebMimeRegistryImpl::mimeTypeForExtension(file_extension); - // The sandbox restricts our access to the registry, so we need to proxy // these calls over to the browser process. std::string mime_type; RenderThread::Get()->Send( new MimeRegistryMsg_GetMimeTypeFromExtension( - base::FilePath::FromUTF16Unsafe(file_extension).value(), &mime_type)); + blink::WebStringToFilePath(file_extension).value(), &mime_type)); return base::ASCIIToUTF16(mime_type); } @@ -521,7 +511,7 @@ bool RendererBlinkPlatformImpl::FileUtilities::getFileInfo( base::File::Info file_info; base::File::Error status = base::File::FILE_ERROR_MAX; if (!SendSyncMessageFromAnyThread(new FileUtilitiesMsg_GetFileInfo( - base::FilePath::FromUTF16Unsafe(path), &file_info, &status)) || + blink::WebStringToFilePath(path), &file_info, &status)) || status != base::File::FILE_OK) { return false; } @@ -595,7 +585,7 @@ void RendererBlinkPlatformImpl::SandboxSupport::getFallbackFontForCharacter( unicode_font_families_.insert(std::make_pair(character, *fallbackFont)); } -void RendererBlinkPlatformImpl::SandboxSupport::getRenderStyleForStrike( +void RendererBlinkPlatformImpl::SandboxSupport::getWebFontRenderStyleForStrike( const char* family, int sizeAndStyle, blink::WebFontRenderStyle* out) { @@ -633,9 +623,12 @@ long long RendererBlinkPlatformImpl::databaseGetFileSize( } long long RendererBlinkPlatformImpl::databaseGetSpaceAvailableForOrigin( - const WebString& origin_identifier) { - return DatabaseUtil::DatabaseGetSpaceAvailable(origin_identifier, - sync_message_filter_.get()); + const blink::WebSecurityOrigin& origin) { + // TODO(jsbell): Pass url::Origin over IPC instead of database + // identifier/GURL. https://crbug.com/591482 + return DatabaseUtil::DatabaseGetSpaceAvailable(WebString::fromUTF8( + storage::GetIdentifierFromOrigin(WebSecurityOriginToGURL(origin))), + sync_message_filter_.get()); } bool RendererBlinkPlatformImpl::databaseSetFileSize( @@ -644,10 +637,16 @@ bool RendererBlinkPlatformImpl::databaseSetFileSize( vfs_file_name, size, sync_message_filter_.get()); } +WebString RendererBlinkPlatformImpl::databaseCreateOriginIdentifier( + const blink::WebSecurityOrigin& origin) { + return WebString::fromUTF8(storage::GetIdentifierFromOrigin( + WebSecurityOriginToGURL(origin))); +} + bool RendererBlinkPlatformImpl::canAccelerate2dCanvas() { RenderThreadImpl* thread = RenderThreadImpl::current(); - GpuChannelHost* host = thread->EstablishGpuChannelSync( - CAUSE_FOR_GPU_LAUNCH_CANVAS_2D); + gpu::GpuChannelHost* host = + thread->EstablishGpuChannelSync(CAUSE_FOR_GPU_LAUNCH_CANVAS_2D); if (!host) return false; @@ -690,7 +689,8 @@ WebAudioDevice* RendererBlinkPlatformImpl::createAudioDevice( unsigned channels, double sample_rate, WebAudioDevice::RenderCallback* callback, - const blink::WebString& input_device_id) { + const blink::WebString& input_device_id, + const blink::WebSecurityOrigin& security_origin) { // Use a mock for testing. blink::WebAudioDevice* mock_device = GetContentClient()->renderer()->OverrideCreateAudioDevice(sample_rate); @@ -749,7 +749,8 @@ WebAudioDevice* RendererBlinkPlatformImpl::createAudioDevice( buffer_size); params.set_channels_for_discrete(channels); - return new RendererWebAudioDeviceImpl(params, callback, session_id); + return new RendererWebAudioDeviceImpl( + params, callback, session_id, static_cast(security_origin)); } bool RendererBlinkPlatformImpl::loadAudioResource( @@ -943,31 +944,34 @@ WebCanvasCaptureHandler* RendererBlinkPlatformImpl::createCanvasCaptureHandler( //------------------------------------------------------------------------------ -blink::WebSpeechSynthesizer* RendererBlinkPlatformImpl::createSpeechSynthesizer( - blink::WebSpeechSynthesizerClient* client) { - return GetContentClient()->renderer()->OverrideSpeechSynthesizer(client); +void RendererBlinkPlatformImpl::createHTMLVideoElementCapturer( + WebMediaStream* web_media_stream, + WebMediaPlayer* web_media_player) { +#if defined(ENABLE_WEBRTC) + DCHECK(web_media_stream); + DCHECK(web_media_player); + AddVideoTrackToMediaStream( + HtmlVideoElementCapturerSource::CreateFromWebMediaPlayerImpl( + web_media_player, + content::RenderThread::Get()->GetIOMessageLoopProxy()), + false, // is_remote + false, // is_readonly + web_media_stream); +#endif } //------------------------------------------------------------------------------ -blink::WebGraphicsContext3D* -RendererBlinkPlatformImpl::createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes) { - return createOffscreenGraphicsContext3D(attributes, NULL); +blink::WebSpeechSynthesizer* RendererBlinkPlatformImpl::createSpeechSynthesizer( + blink::WebSpeechSynthesizerClient* client) { + return GetContentClient()->renderer()->OverrideSpeechSynthesizer(client); } -blink::WebGraphicsContext3D* -RendererBlinkPlatformImpl::createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes, - blink::WebGraphicsContext3D* share_context) { - blink::WebGraphicsContext3D::WebGraphicsInfo gl_info; - return createOffscreenGraphicsContext3D(attributes, share_context, &gl_info); -} +//------------------------------------------------------------------------------ static void Collect3DContextInformationOnFailure( - blink::WebGraphicsContext3D* share_context, - blink::WebGraphicsContext3D::WebGraphicsInfo* gl_info, - GpuChannelHost* host) { + blink::Platform::GraphicsInfo* gl_info, + gpu::GpuChannelHost* host) { DCHECK(gl_info); std::string error_message("OffscreenContext Creation failed, "); if (host) { @@ -1003,44 +1007,67 @@ static void Collect3DContextInformationOnFailure( } } -blink::WebGraphicsContext3D* -RendererBlinkPlatformImpl::createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes, - blink::WebGraphicsContext3D* share_context, - blink::WebGraphicsContext3D::WebGraphicsInfo* gl_info) { +blink::WebGraphicsContext3DProvider* +RendererBlinkPlatformImpl::createOffscreenGraphicsContext3DProvider( + const blink::Platform::ContextAttributes& web_attributes, + const blink::WebURL& top_document_web_url, + blink::WebGraphicsContext3DProvider* share_provider, + blink::Platform::GraphicsInfo* gl_info) { DCHECK(gl_info); if (!RenderThreadImpl::current()) { std::string error_message("Failed to run in Current RenderThreadImpl"); gl_info->errorMessage = WebString::fromUTF8(error_message); - return NULL; + return nullptr; } - scoped_refptr gpu_channel_host( + scoped_refptr gpu_channel_host( RenderThreadImpl::current()->EstablishGpuChannelSync( CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE)); + WebGraphicsContext3DCommandBufferImpl* share_context = + share_provider ? static_cast( + share_provider->context3d()) + : nullptr; + + // This is an offscreen context, which doesn't use the default frame buffer, + // so don't request any alpha, depth, stencil, antialiasing. + gpu::gles2::ContextCreationAttribHelper attributes; + attributes.alpha_size = -1; + attributes.depth_size = 0; + attributes.stencil_size = 0; + attributes.samples = 0; + attributes.sample_buffers = 0; + attributes.bind_generates_resource = false; + + attributes.fail_if_major_perf_caveat = + web_attributes.failIfMajorPerformanceCaveat; + DCHECK_LE(web_attributes.webGLVersion, 2u); + if (web_attributes.webGLVersion == 1) + attributes.context_type = gpu::gles2::CONTEXT_TYPE_WEBGL1; + else if (web_attributes.webGLVersion == 2) + attributes.context_type = gpu::gles2::CONTEXT_TYPE_WEBGL2; + + bool share_resources = false; + bool automatic_flushes = true; + // Prefer discrete GPU for WebGL. + gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits limits; - bool lose_context_when_out_of_memory = false; + scoped_ptr context( WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( - gpu_channel_host.get(), - attributes, - lose_context_when_out_of_memory, - GURL(attributes.topDocumentURL), - limits, - static_cast(share_context))); - - // Most likely the GPU process exited and the attempt to reconnect to it - // failed. Need to try to restore the context again later. - if (!context || !context->InitializeOnCurrentThread() || - gl_info->testFailContext) { + gpu_channel_host.get(), attributes, gpu_preference, share_resources, + automatic_flushes, GURL(top_document_web_url), limits, + share_context)); + scoped_refptr provider = + ContextProviderCommandBuffer::Create(std::move(context), + RENDERER_MAINTHREAD_CONTEXT); + if (!provider || !provider->BindToCurrentThread()) { // Collect Graphicsinfo if there is a context failure or it is failed // purposefully in case of layout tests. - Collect3DContextInformationOnFailure(share_context, gl_info, - gpu_channel_host.get()); - return NULL; + Collect3DContextInformationOnFailure(gl_info, gpu_channel_host.get()); + return nullptr; } - return context.release(); + return new WebGraphicsContext3DProviderImpl(std::move(provider)); } //------------------------------------------------------------------------------ @@ -1049,9 +1076,9 @@ blink::WebGraphicsContext3DProvider* RendererBlinkPlatformImpl::createSharedOffscreenGraphicsContext3DProvider() { scoped_refptr provider = RenderThreadImpl::current()->SharedMainThreadContextProvider(); - if (!provider.get()) - return NULL; - return new WebGraphicsContext3DProviderImpl(provider); + if (!provider) + return nullptr; + return new WebGraphicsContext3DProviderImpl(std::move(provider)); } //------------------------------------------------------------------------------ @@ -1063,9 +1090,8 @@ blink::WebCompositorSupport* RendererBlinkPlatformImpl::compositorSupport() { //------------------------------------------------------------------------------ blink::WebString RendererBlinkPlatformImpl::convertIDNToUnicode( - const blink::WebString& host, - const blink::WebString& languages) { - return url_formatter::IDNToUnicode(host.utf8(), languages.utf8()); + const blink::WebString& host) { + return url_formatter::IDNToUnicode(host.utf8()); } //------------------------------------------------------------------------------ @@ -1165,28 +1191,18 @@ RendererBlinkPlatformImpl::CreatePlatformEventObserverFromType( void RendererBlinkPlatformImpl::SetPlatformEventObserverForTesting( blink::WebPlatformEventType type, scoped_ptr observer) { - DCHECK(type != blink::WebPlatformEventTypeBattery); - if (platform_event_observers_.Lookup(type)) platform_event_observers_.Remove(type); platform_event_observers_.AddWithID(observer.release(), type); } +blink::ServiceRegistry* RendererBlinkPlatformImpl::serviceRegistry() { + return blink_service_registry_.get(); +} + void RendererBlinkPlatformImpl::startListening( blink::WebPlatformEventType type, blink::WebPlatformEventListener* listener) { - if (type == blink::WebPlatformEventTypeBattery) { - if (RenderThreadImpl::current() && - RenderThreadImpl::current()->layout_test_mode()) { - g_test_battery_status_listener = - static_cast(listener); - } else { - battery_status_dispatcher_.reset(new BatteryStatusDispatcher( - static_cast(listener))); - } - return; - } - PlatformEventObserverBase* observer = platform_event_observers_.Lookup(type); if (!observer) { observer = CreatePlatformEventObserverFromType(type); @@ -1246,12 +1262,6 @@ void RendererBlinkPlatformImpl::SendFakeDeviceEventDataForTesting( void RendererBlinkPlatformImpl::stopListening( blink::WebPlatformEventType type) { - if (type == blink::WebPlatformEventTypeBattery) { - g_test_battery_status_listener = nullptr; - battery_status_dispatcher_.reset(); - return; - } - PlatformEventObserverBase* observer = platform_event_observers_.Lookup(type); if (!observer) return; @@ -1276,11 +1286,9 @@ void RendererBlinkPlatformImpl::queryStorageUsageAndQuota( //------------------------------------------------------------------------------ -void RendererBlinkPlatformImpl::MockBatteryStatusChangedForTesting( - const blink::WebBatteryStatus& status) { - if (!g_test_battery_status_listener) - return; - g_test_battery_status_listener->updateBatteryStatus(status); +blink::WebTrialTokenValidator* +RendererBlinkPlatformImpl::trialTokenValidator() { + return &trial_token_validator_; } } // namespace content diff --git a/chromium/content/renderer/renderer_blink_platform_impl.h b/chromium/content/renderer/renderer_blink_platform_impl.h index 8cdb93d4ca5..eb197f84fcf 100644 --- a/chromium/content/renderer/renderer_blink_platform_impl.h +++ b/chromium/content/renderer/renderer_blink_platform_impl.h @@ -16,6 +16,8 @@ #include "cc/blink/web_compositor_support_impl.h" #include "content/child/blink_platform_impl.h" #include "content/common/content_export.h" +#include "content/renderer/origin_trials/web_trial_token_validator_impl.h" +#include "content/renderer/top_level_blame_context.h" #include "content/renderer/webpublicsuffixlist_impl.h" #include "device/vibration/vibration_manager.mojom.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" @@ -31,13 +33,14 @@ class SyncMessageFilter; } namespace blink { -class WebBatteryStatus; class WebCanvasCaptureHandler; class WebDeviceMotionData; class WebDeviceOrientationData; class WebGraphicsContext3DProvider; +class WebMediaPlayer; class WebMediaRecorderHandler; class WebMediaStream; +class WebSecurityOrigin; class WebServiceWorkerCacheStorage; } @@ -47,14 +50,16 @@ class WebThreadImplForRendererScheduler; } namespace content { -class BatteryStatusDispatcher; +class BlinkServiceRegistryImpl; class DeviceLightEventPump; class DeviceMotionEventPump; class DeviceOrientationEventPump; +class LocalStorageCachedAreas; class PlatformEventObserverBase; class QuotaMessageFilter; class RendererClipboardDelegate; class RenderView; +class ServiceRegistry; class ThreadSafeSender; class WebClipboardImpl; class WebDatabaseObserverImpl; @@ -62,8 +67,8 @@ class WebFileSystemImpl; class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { public: - explicit RendererBlinkPlatformImpl( - scheduler::RendererScheduler* renderer_scheduler); + RendererBlinkPlatformImpl(scheduler::RendererScheduler* renderer_scheduler, + base::WeakPtr service_registry); ~RendererBlinkPlatformImpl() override; // Shutdown must be called just prior to shutting down blink. @@ -104,9 +109,12 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { const blink::WebString& vfs_file_name) override; long long databaseGetFileSize(const blink::WebString& vfs_file_name) override; long long databaseGetSpaceAvailableForOrigin( - const blink::WebString& origin_identifier) override; + const blink::WebSecurityOrigin& origin) override; bool databaseSetFileSize(const blink::WebString& vfs_file_name, long long size) override; + blink::WebString databaseCreateOriginIdentifier( + const blink::WebSecurityOrigin& origin) override; + blink::WebString signedPublicKeyAndChallengeString( unsigned key_size_index, const blink::WebString& challenge, @@ -119,8 +127,11 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { blink::WebScrollbarBehavior* scrollbarBehavior() override; blink::WebIDBFactory* idbFactory() override; blink::WebServiceWorkerCacheStorage* cacheStorage( - const blink::WebString& origin_identifier) override; + const blink::WebSecurityOrigin& security_origin) override; blink::WebFileSystem* fileSystem() override; + blink::WebString fileSystemCreateOriginIdentifier( + const blink::WebSecurityOrigin& origin) override; + bool canAccelerate2dCanvas() override; bool isThreadedCompositingEnabled() override; bool isThreadedAnimationEnabled() override; @@ -135,7 +146,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { unsigned channels, double sample_rate, blink::WebAudioDevice::RenderCallback* callback, - const blink::WebString& input_device_id) override; + const blink::WebString& input_device_id, + const blink::WebSecurityOrigin& security_origin) override; bool loadAudioResource(blink::WebAudioBus* destination_bus, const char* audio_file_data, @@ -156,21 +168,19 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { const blink::WebSize& size, double frame_rate, blink::WebMediaStreamTrack* track) override; - blink::WebGraphicsContext3D* createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes) override; - blink::WebGraphicsContext3D* createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes, - blink::WebGraphicsContext3D* share_context) override; - blink::WebGraphicsContext3D* createOffscreenGraphicsContext3D( - const blink::WebGraphicsContext3D::Attributes& attributes, - blink::WebGraphicsContext3D* share_context, - blink::WebGraphicsContext3D::WebGraphicsInfo* gl_info) override; + void createHTMLVideoElementCapturer( + blink::WebMediaStream* web_media_stream, + blink::WebMediaPlayer* web_media_player) override; + blink::WebGraphicsContext3DProvider* createOffscreenGraphicsContext3DProvider( + const blink::Platform::ContextAttributes& attributes, + const blink::WebURL& top_document_web_url, + blink::WebGraphicsContext3DProvider* share_provider, + blink::Platform::GraphicsInfo* gl_info) override; blink::WebGraphicsContext3DProvider* createSharedOffscreenGraphicsContext3DProvider() override; blink::WebCompositorSupport* compositorSupport() override; - blink::WebString convertIDNToUnicode( - const blink::WebString& host, - const blink::WebString& languages) override; + blink::WebString convertIDNToUnicode(const blink::WebString& host) override; + blink::ServiceRegistry* serviceRegistry() override; void startListening(blink::WebPlatformEventType, blink::WebPlatformEventListener*) override; void stopListening(blink::WebPlatformEventType) override; @@ -180,11 +190,12 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void vibrate(unsigned int milliseconds) override; void cancelVibration() override; blink::WebThread* currentThread() override; + blink::BlameContext* topLevelBlameContext() override; void recordRappor(const char* metric, const blink::WebString& sample) override; void recordRapporURL(const char* metric, const blink::WebURL& url) override; - double currentTimeSeconds() override; - double monotonicallyIncreasingTimeSeconds() override; + + blink::WebTrialTokenValidator* trialTokenValidator() override; // Set the PlatformEventObserverBase in |platform_event_observers_| associated // with |type| to |observer|. If there was already an observer associated to @@ -213,10 +224,6 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { static void SetMockDeviceOrientationDataForTesting( const blink::WebDeviceOrientationData& data); - // Notifies blink::WebBatteryStatusListener that battery status has changed. - void MockBatteryStatusChangedForTesting( - const blink::WebBatteryStatus& status); - WebDatabaseObserverImpl* web_database_observer_impl() { return web_database_observer_impl_.get(); } @@ -283,14 +290,19 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { scoped_ptr web_scrollbar_behavior_; - scoped_ptr battery_status_dispatcher_; - // Handle to the Vibration mojo service. device::VibrationManagerPtr vibration_manager_; IDMap platform_event_observers_; scheduler::RendererScheduler* renderer_scheduler_; // NOT OWNED + TopLevelBlameContext top_level_blame_context_; + + WebTrialTokenValidatorImpl trial_token_validator_; + + scoped_ptr local_storage_cached_areas_; + + scoped_ptr blink_service_registry_; DISALLOW_COPY_AND_ASSIGN(RendererBlinkPlatformImpl); }; diff --git a/chromium/content/renderer/renderer_main.cc b/chromium/content/renderer/renderer_main.cc index d97f2f65482..99c55c5477e 100644 --- a/chromium/content/renderer/renderer_main.cc +++ b/chromium/content/renderer/renderer_main.cc @@ -9,24 +9,21 @@ #include "base/command_line.h" #include "base/debug/debugger.h" #include "base/debug/leak_annotations.h" -#include "base/feature_list.h" #include "base/i18n/rtl.h" #include "base/message_loop/message_loop.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/statistics_recorder.h" #include "base/pending_task.h" #include "base/strings/string_util.h" #include "base/sys_info.h" #include "base/threading/platform_thread.h" -#include "base/time/time.h" #include "base/timer/hi_res_timer_manager.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/scheduler/renderer/renderer_scheduler.h" -#include "components/startup_metric_utils/common/startup_metric_messages.h" #include "content/child/child_process.h" #include "content/common/content_constants_internal.h" +#include "content/common/mojo/mojo_shell_connection_impl.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/renderer/content_renderer_client.h" @@ -62,10 +59,6 @@ #include "ui/ozone/public/client_native_pixmap_factory.h" #endif -#if defined(MOJO_SHELL_CLIENT) -#include "content/common/mojo/mojo_shell_connection_impl.h" -#endif - namespace content { namespace { // This function provides some ways to test crash and assertion handling @@ -92,18 +85,13 @@ int RendererMain(const MainFunctionParams& parameters) { // expect synchronous events around the main loop of a thread. TRACE_EVENT_ASYNC_BEGIN0("startup", "RendererMain", 0); - const base::TimeTicks renderer_main_entry_time = base::TimeTicks::Now(); - base::trace_event::TraceLog::GetInstance()->SetProcessName("Renderer"); base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex( kTraceEventRendererProcessSortIndex); const base::CommandLine& parsed_command_line = parameters.command_line; -#if defined(MOJO_SHELL_CLIENT) - if (parsed_command_line.HasSwitch(switches::kEnableMojoShellConnection)) - MojoShellConnectionImpl::Create(); -#endif + MojoShellConnectionImpl::Create(); #if defined(OS_MACOSX) base::mac::ScopedNSAutoreleasePool* pool = parameters.autorelease_pool; @@ -165,25 +153,6 @@ int RendererMain(const MainFunctionParams& parameters) { base::android::RecordChromiumAndroidLinkerRendererHistogram(); #endif - // Initialize statistical testing infrastructure. We set the entropy provider - // to NULL to disallow the renderer process from creating its own one-time - // randomized trials; they should be created in the browser process. - base::FieldTrialList field_trial_list(NULL); - // Ensure any field trials in browser are reflected into renderer. - if (parsed_command_line.HasSwitch(switches::kForceFieldTrials)) { - bool result = base::FieldTrialList::CreateTrialsFromString( - parsed_command_line.GetSwitchValueASCII(switches::kForceFieldTrials), - base::FieldTrialList::DONT_ACTIVATE_TRIALS, - std::set()); - DCHECK(result); - } - - scoped_ptr feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - parsed_command_line.GetSwitchValueASCII(switches::kEnableFeatures), - parsed_command_line.GetSwitchValueASCII(switches::kDisableFeatures)); - base::FeatureList::SetInstance(std::move(feature_list)); - scoped_ptr renderer_scheduler( scheduler::RendererScheduler::Create()); @@ -218,9 +187,6 @@ int RendererMain(const MainFunctionParams& parameters) { RenderThreadImpl::Create(std::move(main_message_loop), std::move(renderer_scheduler)); #endif - RenderThreadImpl::current()->Send( - new StartupMetricHostMsg_RecordRendererMainEntryTime( - renderer_main_entry_time)); base::HighResolutionTimerManager hi_res_timer_manager; @@ -233,6 +199,9 @@ int RendererMain(const MainFunctionParams& parameters) { base::MessageLoop::current()->Run(); TRACE_EVENT_ASYNC_END0("toplevel", "RendererMain.START_MSG_LOOP", 0); } + + MojoShellConnectionImpl::Destroy(); + #if defined(LEAK_SANITIZER) // Run leak detection before RenderProcessImpl goes out of scope. This helps // ignore shutdown-only leaks. diff --git a/chromium/content/renderer/renderer_main_platform_delegate_linux.cc b/chromium/content/renderer/renderer_main_platform_delegate_linux.cc index 4a632b3d51c..5743abd8a4f 100644 --- a/chromium/content/renderer/renderer_main_platform_delegate_linux.cc +++ b/chromium/content/renderer/renderer_main_platform_delegate_linux.cc @@ -30,7 +30,7 @@ void RendererMainPlatformDelegate::PlatformUninitialize() { bool RendererMainPlatformDelegate::EnableSandbox() { // The setuid sandbox is started in the zygote process: zygote_main_linux.cc - // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox + // https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox.md // // Anything else is started in InitializeSandbox(). LinuxSandbox::InitializeSandbox(); diff --git a/chromium/content/renderer/renderer_main_platform_delegate_win.cc b/chromium/content/renderer/renderer_main_platform_delegate_win.cc index ae7e7e47f4b..25381a5efaa 100644 --- a/chromium/content/renderer/renderer_main_platform_delegate_win.cc +++ b/chromium/content/renderer/renderer_main_platform_delegate_win.cc @@ -14,9 +14,8 @@ #include "base/win/win_util.h" #include "base/win/windows_version.h" #include "content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.h" -#include "content/common/font_warmup_win.h" +#include "content/child/font_warmup_win.h" #include "content/public/common/content_switches.h" -#include "content/public/common/dwrite_font_platform_win.h" #include "content/public/common/injection_test_win.h" #include "content/public/renderer/render_thread.h" #include "content/renderer/render_thread_impl.h" @@ -40,12 +39,6 @@ void SkiaPreCacheFont(const LOGFONT& logfont) { } } -// Helper function to cast RenderThread to IPC::Sender so we can Bind() -// it. -IPC::Sender* GetRenderThreadSender() { - return RenderThread::Get(); -} - } // namespace RendererMainPlatformDelegate::RendererMainPlatformDelegate( @@ -75,10 +68,7 @@ void RendererMainPlatformDelegate::PlatformInitialize() { scoped_ptr zone(icu::TimeZone::createDefault()); if (use_direct_write) { - if (ShouldUseDirectWriteFontProxyFieldTrial()) - InitializeDWriteFontProxy(base::Bind(&GetRenderThreadSender)); - else - WarmupDirectWrite(); + InitializeDWriteFontProxy(); } else { SkTypeface_SetEnsureLOGFONTAccessibleProc(SkiaPreCacheFont); } @@ -88,8 +78,7 @@ void RendererMainPlatformDelegate::PlatformInitialize() { } void RendererMainPlatformDelegate::PlatformUninitialize() { - if (ShouldUseDirectWriteFontProxyFieldTrial()) - UninitializeDWriteFontProxy(); + UninitializeDWriteFontProxy(); } bool RendererMainPlatformDelegate::EnableSandbox() { diff --git a/chromium/content/renderer/renderer_webapplicationcachehost_impl.cc b/chromium/content/renderer/renderer_webapplicationcachehost_impl.cc index 42aae612c26..817d69d76d8 100644 --- a/chromium/content/renderer/renderer_webapplicationcachehost_impl.cc +++ b/chromium/content/renderer/renderer_webapplicationcachehost_impl.cc @@ -20,8 +20,7 @@ RendererWebApplicationCacheHostImpl::RendererWebApplicationCacheHostImpl( WebApplicationCacheHostClient* client, AppCacheBackend* backend) : WebApplicationCacheHostImpl(client, backend), - routing_id_(render_view->routing_id()) { -} + routing_id_(render_view->GetRoutingID()) {} void RendererWebApplicationCacheHostImpl::OnLogMessage( AppCacheLogLevel log_level, const std::string& message) { diff --git a/chromium/content/renderer/renderer_webcookiejar_impl.cc b/chromium/content/renderer/renderer_webcookiejar_impl.cc index 1ae89b4fca0..36706a0605b 100644 --- a/chromium/content/renderer/renderer_webcookiejar_impl.cc +++ b/chromium/content/renderer/renderer_webcookiejar_impl.cc @@ -30,11 +30,6 @@ WebString RendererWebCookieJarImpl::cookies( return WebString::fromUTF8(value_utf8); } -WebString RendererWebCookieJarImpl::cookieRequestHeaderFieldValue( - const WebURL& url, const WebURL& first_party_for_cookies) { - return cookies(url, first_party_for_cookies); -} - bool RendererWebCookieJarImpl::cookiesEnabled( const WebURL& url, const WebURL& first_party_for_cookies) { bool cookies_enabled = false; diff --git a/chromium/content/renderer/renderer_webcookiejar_impl.h b/chromium/content/renderer/renderer_webcookiejar_impl.h index 1adbbaea2dc..dce3aeca18c 100644 --- a/chromium/content/renderer/renderer_webcookiejar_impl.h +++ b/chromium/content/renderer/renderer_webcookiejar_impl.h @@ -26,9 +26,6 @@ class RendererWebCookieJarImpl : public blink::WebCookieJar { blink::WebString cookies( const blink::WebURL& url, const blink::WebURL& first_party_for_cookies) override; - blink::WebString cookieRequestHeaderFieldValue( - const blink::WebURL& url, - const blink::WebURL& first_party_for_cookies) override; bool cookiesEnabled(const blink::WebURL& url, const blink::WebURL& first_party_for_cookies) override; diff --git a/chromium/content/renderer/resizing_mode_selector.cc b/chromium/content/renderer/resizing_mode_selector.cc index 58b4af1c02a..20c1cc1bd25 100644 --- a/chromium/content/renderer/resizing_mode_selector.cc +++ b/chromium/content/renderer/resizing_mode_selector.cc @@ -18,9 +18,8 @@ bool ResizingModeSelector::NeverUsesSynchronousResize() const { !RenderThreadImpl::current()->layout_test_mode(); } -bool ResizingModeSelector::ShouldAbortOnResize( - RenderWidget* widget, - const ViewMsg_Resize_Params& params) { +bool ResizingModeSelector::ShouldAbortOnResize(RenderWidget* widget, + const ResizeParams& params) { return is_synchronous_mode_ && params.is_fullscreen_granted == widget->is_fullscreen_granted() && params.display_mode == widget->display_mode() && diff --git a/chromium/content/renderer/resizing_mode_selector.h b/chromium/content/renderer/resizing_mode_selector.h index 66a62bcd50b..7b92b9d70e4 100644 --- a/chromium/content/renderer/resizing_mode_selector.h +++ b/chromium/content/renderer/resizing_mode_selector.h @@ -7,11 +7,10 @@ #include "base/macros.h" -struct ViewMsg_Resize_Params; - namespace content { class RenderWidget; +struct ResizeParams; // Enables switching between two modes of resizing: // 1) The "normal" (asynchronous) resizing, which involves sending messages to @@ -26,8 +25,7 @@ class ResizingModeSelector { public: ResizingModeSelector(); bool NeverUsesSynchronousResize() const; - bool ShouldAbortOnResize(RenderWidget* widget, - const ViewMsg_Resize_Params& params); + bool ShouldAbortOnResize(RenderWidget* widget, const ResizeParams& params); void set_is_synchronous_mode(bool mode); bool is_synchronous_mode() const; diff --git a/chromium/content/renderer/savable_resources.cc b/chromium/content/renderer/savable_resources.cc index eb6c9da99ec..c1a702bd0fc 100644 --- a/chromium/content/renderer/savable_resources.cc +++ b/chromium/content/renderer/savable_resources.cc @@ -61,13 +61,9 @@ void GetSavableResourceLinkForElement( const WebElement& element, const WebDocument& current_doc, SavableResourcesResult* result) { - // Check whether the node has sub resource URL or not. - WebString value = GetSubResourceLinkFromElement(element); - if (value.isNull()) - return; - // Get absolute URL. - GURL element_url = current_doc.completeURL(value); + WebString link_attribute_value = GetSubResourceLinkFromElement(element); + GURL element_url = current_doc.completeURL(link_attribute_value); // See whether to report this element as a subframe. WebFrame* web_frame = WebFrame::fromFrameOwnerElement(element); @@ -79,6 +75,10 @@ void GetSavableResourceLinkForElement( return; } + // Check whether the node has sub resource URL or not. + if (link_attribute_value.isNull()) + return; + // Ignore invalid URL. if (!element_url.is_valid()) return; diff --git a/chromium/content/renderer/scheduler/resource_dispatch_throttler.cc b/chromium/content/renderer/scheduler/resource_dispatch_throttler.cc index aae15adaace..10e920f3b1f 100644 --- a/chromium/content/renderer/scheduler/resource_dispatch_throttler.cc +++ b/chromium/content/renderer/scheduler/resource_dispatch_throttler.cc @@ -66,13 +66,16 @@ bool ResourceDispatchThrottler::Send(IPC::Message* msg) { if (!IsResourceRequest(*msg)) return ForwardMessage(msg); - if (!scheduler_->IsHighPriorityWorkAnticipated()) + if (!scheduler_->IsHighPriorityWorkAnticipated()) { + // Treat an unthrottled request as a flush. + LogFlush(); return ForwardMessage(msg); + } - if (Now() > (last_sent_request_time_ + flush_period_)) { - // If sufficient time has passed since the previous send, we can effectively - // mark the pipeline as flushed. - sent_requests_since_last_flush_ = 0; + if (Now() > (last_flush_time_ + flush_period_)) { + // If sufficient time has passed since the previous flush, we can + // effectively mark the pipeline as flushed. + LogFlush(); return ForwardMessage(msg); } @@ -99,7 +102,7 @@ void ResourceDispatchThrottler::Flush() { DCHECK(thread_checker_.CalledOnValidThread()); TRACE_EVENT1("loader", "ResourceDispatchThrottler::Flush", "total_throttled_messages", throttled_messages_.size()); - sent_requests_since_last_flush_ = 0; + LogFlush(); // If high-priority work is no longer anticipated, dispatch can be safely // accelerated. Avoid completely flushing in such case in the event that @@ -121,6 +124,7 @@ void ResourceDispatchThrottler::Flush() { } void ResourceDispatchThrottler::FlushAll() { + LogFlush(); if (throttled_messages_.empty()) return; @@ -135,11 +139,15 @@ void ResourceDispatchThrottler::FlushAll() { DCHECK(throttled_messages_.empty()); } +void ResourceDispatchThrottler::LogFlush() { + sent_requests_since_last_flush_ = 0; + last_flush_time_ = Now(); +} + bool ResourceDispatchThrottler::ForwardMessage(IPC::Message* msg) { - if (IsResourceRequest(*msg)) { - last_sent_request_time_ = Now(); + if (IsResourceRequest(*msg)) ++sent_requests_since_last_flush_; - } + return proxied_sender_->Send(msg); } diff --git a/chromium/content/renderer/scheduler/resource_dispatch_throttler.h b/chromium/content/renderer/scheduler/resource_dispatch_throttler.h index d7f89b72eb7..bf2e77f241b 100644 --- a/chromium/content/renderer/scheduler/resource_dispatch_throttler.h +++ b/chromium/content/renderer/scheduler/resource_dispatch_throttler.h @@ -53,6 +53,7 @@ class CONTENT_EXPORT ResourceDispatchThrottler : public IPC::Sender { void Flush(); void FlushAll(); + void LogFlush(); bool ForwardMessage(IPC::Message* msg); base::ThreadChecker thread_checker_; @@ -63,7 +64,7 @@ class CONTENT_EXPORT ResourceDispatchThrottler : public IPC::Sender { const uint32_t max_requests_per_flush_; base::Timer flush_timer_; - base::TimeTicks last_sent_request_time_; + base::TimeTicks last_flush_time_; uint32_t sent_requests_since_last_flush_; std::deque throttled_messages_; diff --git a/chromium/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc b/chromium/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc index a1dbcac7076..15a233430c4 100644 --- a/chromium/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc +++ b/chromium/content/renderer/scheduler/resource_dispatch_throttler_unittest.cc @@ -75,6 +75,7 @@ class ResourceDispatchThrottlerForTest : public ResourceDispatchThrottler { scheduler, base::TimeDelta::FromSecondsD(kFlushPeriodSeconds), kRequestsPerFlush), + now_(base::TimeTicks() + base::TimeDelta::FromDays(1)), flush_scheduled_(false) {} ~ResourceDispatchThrottlerForTest() override {} @@ -252,6 +253,45 @@ TEST_F(ResourceDispatchThrottlerTest, NotThrottledIfSufficientTimePassed) { } } +TEST_F(ResourceDispatchThrottlerTest, NotThrottledIfSendRateSufficientlyLow) { + SetHighPriorityWorkAnticipated(true); + + // Continuous dispatch of resource requests below the allowed send rate + // should never throttled. + const base::TimeDelta kAllowedContinuousSendInterval = + base::TimeDelta::FromSecondsD((kFlushPeriodSeconds / kRequestsPerFlush) + + .00001); + for (size_t i = 0; i < kRequestsPerFlush * 10; ++i) { + Advance(kAllowedContinuousSendInterval); + RequestResource(); + EXPECT_EQ(1U, GetAndResetSentMessageCount()); + EXPECT_FALSE(FlushScheduled()); + } +} + +TEST_F(ResourceDispatchThrottlerTest, ThrottledIfSendRateSufficientlyHigh) { + SetHighPriorityWorkAnticipated(true); + + // Continuous dispatch of resource requests above the allowed send rate + // should be throttled. + const base::TimeDelta kThrottledContinuousSendInterval = + base::TimeDelta::FromSecondsD((kFlushPeriodSeconds / kRequestsPerFlush) - + .00001); + + for (size_t i = 0; i < kRequestsPerFlush * 10; ++i) { + Advance(kThrottledContinuousSendInterval); + RequestResource(); + // Only the first batch of requests under the limit should be unthrottled. + if (i < kRequestsPerFlush) { + EXPECT_EQ(1U, GetAndResetSentMessageCount()); + EXPECT_FALSE(FlushScheduled()); + } else { + EXPECT_EQ(0U, GetAndResetSentMessageCount()); + EXPECT_TRUE(FlushScheduled()); + } + } +} + TEST_F(ResourceDispatchThrottlerTest, NotThrottledIfSyncMessage) { SetHighPriorityWorkAnticipated(true); diff --git a/chromium/content/renderer/service_worker/embedded_worker_devtools_agent.cc b/chromium/content/renderer/service_worker/embedded_worker_devtools_agent.cc index a09972937e2..6d44d5f29ee 100644 --- a/chromium/content/renderer/service_worker/embedded_worker_devtools_agent.cc +++ b/chromium/content/renderer/service_worker/embedded_worker_devtools_agent.cc @@ -6,7 +6,6 @@ #include "content/common/devtools_messages.h" #include "content/renderer/render_thread_impl.h" -#include "third_party/WebKit/public/platform/WebCString.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebEmbeddedWorker.h" diff --git a/chromium/content/renderer/service_worker/embedded_worker_dispatcher.cc b/chromium/content/renderer/service_worker/embedded_worker_dispatcher.cc index c99168cd0cf..f4334ede37e 100644 --- a/chromium/content/renderer/service_worker/embedded_worker_dispatcher.cc +++ b/chromium/content/renderer/service_worker/embedded_worker_dispatcher.cc @@ -53,6 +53,8 @@ bool EmbeddedWorkerDispatcher::OnMessageReceived( IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDispatcher, message) IPC_MESSAGE_HANDLER(EmbeddedWorkerMsg_StartWorker, OnStartWorker) IPC_MESSAGE_HANDLER(EmbeddedWorkerMsg_StopWorker, OnStopWorker) + IPC_MESSAGE_HANDLER(EmbeddedWorkerMsg_ResumeAfterDownload, + OnResumeAfterDownload) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -76,7 +78,6 @@ void EmbeddedWorkerDispatcher::OnStartWorker( const EmbeddedWorkerMsg_StartWorker_Params& params) { DCHECK(!workers_.Lookup(params.embedded_worker_id)); TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerDispatcher::OnStartWorker"); - RenderThread::Get()->EnsureWebKitInitialized(); scoped_ptr wrapper( new WorkerWrapper(blink::WebEmbeddedWorker::create( new ServiceWorkerContextClient( @@ -95,8 +96,13 @@ void EmbeddedWorkerDispatcher::OnStartWorker( params.wait_for_debugger ? blink::WebEmbeddedWorkerStartData::WaitForDebugger : blink::WebEmbeddedWorkerStartData::DontWaitForDebugger; - start_data.v8CacheOptions = - static_cast(params.v8_cache_options); + start_data.v8CacheOptions = static_cast( + params.settings.v8_cache_options); + start_data.dataSaverEnabled = params.settings.data_saver_enabled; + start_data.pauseAfterDownloadMode = + params.pause_after_download + ? blink::WebEmbeddedWorkerStartData::PauseAfterDownload + : blink::WebEmbeddedWorkerStartData::DontPauseAfterDownload; wrapper->worker()->startWorkerContext(start_data); workers_.AddWithID(wrapper.release(), params.embedded_worker_id); @@ -117,4 +123,15 @@ void EmbeddedWorkerDispatcher::OnStopWorker(int embedded_worker_id) { wrapper->worker()->terminateWorkerContext(); } +void EmbeddedWorkerDispatcher::OnResumeAfterDownload(int embedded_worker_id) { + TRACE_EVENT0("ServiceWorker", + "EmbeddedWorkerDispatcher::OnResumeAfterDownload"); + WorkerWrapper* wrapper = workers_.Lookup(embedded_worker_id); + if (!wrapper) { + LOG(WARNING) << "Got OnResumeAfterDownload for nonexistent worker"; + return; + } + wrapper->worker()->resumeAfterDownload(); +} + } // namespace content diff --git a/chromium/content/renderer/service_worker/embedded_worker_dispatcher.h b/chromium/content/renderer/service_worker/embedded_worker_dispatcher.h index 2ba1da86b38..f118d587c12 100644 --- a/chromium/content/renderer/service_worker/embedded_worker_dispatcher.h +++ b/chromium/content/renderer/service_worker/embedded_worker_dispatcher.h @@ -34,6 +34,7 @@ class EmbeddedWorkerDispatcher : public IPC::Listener { void OnStartWorker(const EmbeddedWorkerMsg_StartWorker_Params& params); void OnStopWorker(int embedded_worker_id); + void OnResumeAfterDownload(int embedded_worker_id); IDMap workers_; std::map stop_worker_times_; diff --git a/chromium/content/renderer/service_worker/service_worker_context_client.cc b/chromium/content/renderer/service_worker/service_worker_context_client.cc index 9119c5712fa..8d566ab7977 100644 --- a/chromium/content/renderer/service_worker/service_worker_context_client.cc +++ b/chromium/content/renderer/service_worker/service_worker_context_client.cc @@ -4,6 +4,7 @@ #include "content/renderer/service_worker/service_worker_context_client.h" +#include #include #include "base/lazy_instance.h" @@ -14,10 +15,10 @@ #include "base/threading/thread_checker.h" #include "base/threading/thread_local.h" #include "base/trace_event/trace_event.h" -#include "content/child/navigator_connect/service_port_dispatcher_impl.h" #include "content/child/notifications/notification_data_conversions.h" #include "content/child/request_extra_data.h" #include "content/child/service_worker/service_worker_dispatcher.h" +#include "content/child/service_worker/service_worker_handle_reference.h" #include "content/child/service_worker/service_worker_network_provider.h" #include "content/child/service_worker/service_worker_provider_context.h" #include "content/child/service_worker/service_worker_registration_handle_reference.h" @@ -32,6 +33,7 @@ #include "content/common/mojo/service_registry_impl.h" #include "content/common/service_worker/embedded_worker_messages.h" #include "content/common/service_worker/service_worker_messages.h" +#include "content/public/common/push_event_payload.h" #include "content/public/common/referrer.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/document_state.h" @@ -42,10 +44,10 @@ #include "content/renderer/service_worker/service_worker_type_util.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" -#include "third_party/WebKit/public/platform/WebCrossOriginServiceWorkerClient.h" +#include "third_party/WebKit/public/platform/URLConversion.h" #include "third_party/WebKit/public/platform/WebMessagePortChannel.h" -#include "third_party/WebKit/public/platform/WebPassOwnPtr.h" #include "third_party/WebKit/public/platform/WebReferrerPolicy.h" +#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/modules/background_sync/WebSyncRegistration.h" #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationData.h" @@ -94,9 +96,10 @@ class WebServiceWorkerNetworkProviderImpl blink::WebURLRequest& request) override { ServiceWorkerNetworkProvider* provider = ServiceWorkerNetworkProvider::FromDocumentState( - static_cast(data_source->extraData())); + static_cast(data_source->getExtraData())); scoped_ptr extra_data(new RequestExtraData); extra_data->set_service_worker_provider_id(provider->provider_id()); + extra_data->set_originated_from_service_worker(true); request.setExtraData(extra_data.release()); } }; @@ -112,16 +115,6 @@ void SendPostMessageToClientOnMainThread( WebMessagePortChannelImpl::ExtractMessagePortIDs(std::move(channels)))); } -void SendCrossOriginMessageToClientOnMainThread( - ThreadSafeSender* sender, - int message_port_id, - const base::string16& message, - scoped_ptr channels) { - sender->Send(new MessagePortHostMsg_PostMessage( - message_port_id, MessagePortMessage(message), - WebMessagePortChannelImpl::ExtractMessagePortIDs(std::move(channels)))); -} - blink::WebURLRequest::FetchRequestMode GetBlinkFetchRequestMode( FetchRequestMode mode) { return static_cast(mode); @@ -179,7 +172,7 @@ struct ServiceWorkerContextClient::WorkerContextData { using SkipWaitingCallbacksMap = IDMap; using SyncEventCallbacksMap = - IDMap, + IDMap, IDMapOwnPointer>; explicit WorkerContextData(ServiceWorkerContextClient* owner) @@ -250,15 +243,18 @@ void ServiceWorkerContextClient::OnMessageReceived( bool handled = true; IPC_BEGIN_MESSAGE_MAP(ServiceWorkerContextClient, message) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ActivateEvent, OnActivateEvent) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ExtendableMessageEvent, + OnExtendableMessageEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_FetchEvent, OnFetchEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_InstallEvent, OnInstallEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_NotificationClickEvent, OnNotificationClickEvent) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_NotificationCloseEvent, + OnNotificationCloseEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_PushEvent, OnPushEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GeofencingEvent, OnGeofencingEvent) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToWorker, OnPostMessage) - IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CrossOriginMessageToWorker, - OnCrossOriginMessageToWorker) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetClient, OnDidGetClient) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetClients, OnDidGetClients) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_OpenWindowResponse, OnOpenWindowResponse) @@ -280,8 +276,8 @@ void ServiceWorkerContextClient::OnMessageReceived( } void ServiceWorkerContextClient::BindServiceRegistry( - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services) { + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services) { context_->service_registry.Bind(std::move(services)); context_->service_registry.BindRemoteServiceProvider( std::move(exposed_services)); @@ -291,6 +287,15 @@ blink::WebURL ServiceWorkerContextClient::scope() const { return service_worker_scope_; } +void ServiceWorkerContextClient::getClient( + const blink::WebString& id, + blink::WebServiceWorkerClientCallbacks* callbacks) { + DCHECK(callbacks); + int request_id = context_->client_callbacks.Add(callbacks); + Send(new ServiceWorkerHostMsg_GetClient( + GetRoutingID(), request_id, base::UTF16ToUTF8(base::StringPiece16(id)))); +} + void ServiceWorkerContextClient::getClients( const blink::WebServiceWorkerClientQueryOptions& weboptions, blink::WebServiceWorkerClientsCallbacks* callbacks) { @@ -370,9 +375,6 @@ void ServiceWorkerContextClient::workerContextStarted( kInvalidServiceWorkerRegistrationId); // Register Mojo services. - context_->service_registry.ServiceRegistry::AddService( - base::Bind(&ServicePortDispatcherImpl::Create, - context_->proxy_weak_factory.GetWeakPtr())); context_->service_registry.ServiceRegistry::AddService( base::Bind(&BackgroundSyncClientImpl::Create)); @@ -402,11 +404,7 @@ void ServiceWorkerContextClient::didEvaluateWorkerScript(bool success) { } void ServiceWorkerContextClient::didInitializeWorkerContext( - v8::Local context, - const blink::WebURL& url) { - // TODO(annekao): Remove WebURL parameter from Blink, it's at best redundant - // given |script_url_|, and may be empty in the future. - // Also remove m_documentURL from ServiceWorkerGlobalScopeProxy. + v8::Local context) { GetContentClient() ->renderer() ->DidInitializeServiceWorkerContextOnWorkerThread(context, script_url_); @@ -448,10 +446,8 @@ void ServiceWorkerContextClient::reportException( int column_number, const blink::WebString& source_url) { Send(new EmbeddedWorkerHostMsg_ReportException( - embedded_worker_id_, - error_message, - line_number, - column_number, GURL(source_url))); + embedded_worker_id_, error_message, line_number, column_number, + blink::WebStringToGURL(source_url))); } void ServiceWorkerContextClient::reportConsoleMessage( @@ -465,7 +461,7 @@ void ServiceWorkerContextClient::reportConsoleMessage( params.message_level = level; params.message = message; params.line_number = line_number; - params.source_url = GURL(source_url); + params.source_url = blink::WebStringToGURL(source_url); Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage( embedded_worker_id_, params)); @@ -488,6 +484,13 @@ void ServiceWorkerContextClient::didHandleActivateEvent( GetRoutingID(), request_id, result)); } +void ServiceWorkerContextClient::didHandleExtendableMessageEvent( + int request_id, + blink::WebServiceWorkerEventResult result) { + Send(new ServiceWorkerHostMsg_ExtendableMessageEventFinished( + GetRoutingID(), request_id, result)); +} + void ServiceWorkerContextClient::didHandleInstallEvent( int request_id, blink::WebServiceWorkerEventResult result) { @@ -511,7 +514,10 @@ void ServiceWorkerContextClient::didHandleFetchEvent( web_response.url(), web_response.status(), web_response.statusText().utf8(), web_response.responseType(), headers, web_response.blobUUID().utf8(), web_response.blobSize(), - web_response.streamURL(), web_response.error()); + web_response.streamURL(), web_response.error(), + base::Time::FromInternalValue(web_response.responseTime()), + !web_response.cacheStorageCacheName().isNull(), + web_response.cacheStorageCacheName().utf8()); Send(new ServiceWorkerHostMsg_FetchEventFinished( GetRoutingID(), request_id, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, @@ -522,7 +528,14 @@ void ServiceWorkerContextClient::didHandleNotificationClickEvent( int request_id, blink::WebServiceWorkerEventResult result) { Send(new ServiceWorkerHostMsg_NotificationClickEventFinished( - GetRoutingID(), request_id)); + GetRoutingID(), request_id, result)); +} + +void ServiceWorkerContextClient::didHandleNotificationCloseEvent( + int request_id, + blink::WebServiceWorkerEventResult result) { + Send(new ServiceWorkerHostMsg_NotificationCloseEventFinished( + GetRoutingID(), request_id, result)); } void ServiceWorkerContextClient::didHandlePushEvent( @@ -540,9 +553,9 @@ void ServiceWorkerContextClient::didHandleSyncEvent( if (!callback) return; if (result == blink::WebServiceWorkerEventResultCompleted) { - callback->Run(SERVICE_WORKER_EVENT_STATUS_COMPLETED); + callback->Run(mojom::ServiceWorkerEventStatus::COMPLETED); } else { - callback->Run(SERVICE_WORKER_EVENT_STATUS_REJECTED); + callback->Run(mojom::ServiceWorkerEventStatus::REJECTED); } context_->sync_event_callbacks.Remove(request_id); } @@ -593,30 +606,18 @@ void ServiceWorkerContextClient::postMessageToClient( // to overtake those messages. scoped_ptr channel_array(channels); main_thread_task_runner_->PostTask( - FROM_HERE, - base::Bind(&SendPostMessageToClientOnMainThread, - sender_, - GetRoutingID(), - base::UTF16ToUTF8(base::StringPiece16(uuid)), - static_cast(message), - base::Passed(&channel_array))); + FROM_HERE, base::Bind(&SendPostMessageToClientOnMainThread, + base::RetainedRef(sender_), GetRoutingID(), + base::UTF16ToUTF8(base::StringPiece16(uuid)), + static_cast(message), + base::Passed(&channel_array))); } void ServiceWorkerContextClient::postMessageToCrossOriginClient( - const blink::WebCrossOriginServiceWorkerClient& client, - const blink::WebString& message, - blink::WebMessagePortChannelArray* channels) { - // This may send channels for MessagePorts, and all internal book-keeping - // messages for MessagePort (e.g. QueueMessages) are sent from main thread - // (with thread hopping), so we need to do the same thread hopping here not - // to overtake those messages. - scoped_ptr channel_array(channels); - main_thread_task_runner_->PostTask( - FROM_HERE, - base::Bind(&SendCrossOriginMessageToClientOnMainThread, - sender_, client.clientID, - static_cast(message), - base::Passed(&channel_array))); + const blink::WebCrossOriginServiceWorkerClient&, + const blink::WebString&, + blink::WebMessagePortChannelArray*) { + NOTREACHED(); } void ServiceWorkerContextClient::focus( @@ -655,20 +656,26 @@ void ServiceWorkerContextClient::claim( } void ServiceWorkerContextClient::registerForeignFetchScopes( - const blink::WebVector& sub_scopes) { + const blink::WebVector& sub_scopes, + const blink::WebVector& origins) { Send(new ServiceWorkerHostMsg_RegisterForeignFetchScopes( - GetRoutingID(), std::vector(sub_scopes.begin(), sub_scopes.end()))); + GetRoutingID(), std::vector(sub_scopes.begin(), sub_scopes.end()), + std::vector(origins.begin(), origins.end()))); } void ServiceWorkerContextClient::DispatchSyncEvent( - const blink::WebSyncRegistration& registration, + const std::string& tag, blink::WebServiceWorkerContextProxy::LastChanceOption last_chance, const SyncCallback& callback) { TRACE_EVENT0("ServiceWorker", "ServiceWorkerContextClient::DispatchSyncEvent"); int request_id = context_->sync_event_callbacks.Add(new SyncCallback(callback)); - proxy_->dispatchSyncEvent(request_id, registration, last_chance); + + // TODO(jkarlin): Make this blink::WebString::FromUTF8Lenient once + // https://crrev.com/1768063002/ lands. + proxy_->dispatchSyncEvent(request_id, blink::WebString::fromUTF8(tag), + last_chance); } void ServiceWorkerContextClient::Send(IPC::Message* message) { @@ -706,6 +713,37 @@ void ServiceWorkerContextClient::OnActivateEvent(int request_id) { proxy_->dispatchActivateEvent(request_id); } +void ServiceWorkerContextClient::OnExtendableMessageEvent( + int request_id, + const ServiceWorkerMsg_ExtendableMessageEvent_Params& params) { + TRACE_EVENT0("ServiceWorker", + "ServiceWorkerContextClient::OnExtendableMessageEvent"); + blink::WebMessagePortChannelArray ports = + WebMessagePortChannelImpl::CreatePorts(params.message_ports, + params.new_routing_ids, + main_thread_task_runner_); + if (params.source.client_info.IsValid()) { + blink::WebServiceWorkerClientInfo web_client = + ToWebServiceWorkerClientInfo(params.source.client_info); + proxy_->dispatchExtendableMessageEvent( + request_id, params.message, params.source_origin, ports, web_client); + return; + } + + DCHECK(params.source.service_worker_info.IsValid()); + scoped_ptr handle = + ServiceWorkerHandleReference::Adopt(params.source.service_worker_info, + sender_.get()); + ServiceWorkerDispatcher* dispatcher = + ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance( + sender_.get(), main_thread_task_runner_.get()); + scoped_refptr worker = + dispatcher->GetOrCreateServiceWorker(std::move(handle)); + proxy_->dispatchExtendableMessageEvent( + request_id, params.message, params.source_origin, ports, + WebServiceWorkerImpl::CreateHandle(worker)); +} + void ServiceWorkerContextClient::OnInstallEvent(int request_id) { TRACE_EVENT0("ServiceWorker", "ServiceWorkerContextClient::OnInstallEvent"); @@ -743,7 +781,11 @@ void ServiceWorkerContextClient::OnFetchEvent( webRequest.setFrameType(GetBlinkFrameType(request.frame_type)); webRequest.setClientId(blink::WebString::fromUTF8(request.client_id)); webRequest.setIsReload(request.is_reload); - proxy_->dispatchFetchEvent(request_id, webRequest); + if (request.fetch_type == ServiceWorkerFetchType::FOREIGN_FETCH) { + proxy_->dispatchForeignFetchEvent(request_id, webRequest); + } else { + proxy_->dispatchFetchEvent(request_id, webRequest); + } } void ServiceWorkerContextClient::OnNotificationClickEvent( @@ -760,11 +802,26 @@ void ServiceWorkerContextClient::OnNotificationClickEvent( action_index); } +void ServiceWorkerContextClient::OnNotificationCloseEvent( + int request_id, + int64_t persistent_notification_id, + const PlatformNotificationData& notification_data) { + TRACE_EVENT0("ServiceWorker", + "ServiceWorkerContextClient::OnNotificationCloseEvent"); + proxy_->dispatchNotificationCloseEvent( + request_id, persistent_notification_id, + ToWebNotificationData(notification_data)); +} + void ServiceWorkerContextClient::OnPushEvent(int request_id, - const std::string& data) { + const PushEventPayload& payload) { TRACE_EVENT0("ServiceWorker", "ServiceWorkerContextClient::OnPushEvent"); - proxy_->dispatchPushEvent(request_id, blink::WebString::fromUTF8(data)); + // Only set data to be a valid string if the payload had decrypted data. + blink::WebString data; + if (!payload.is_null) + data.assign(blink::WebString::fromUTF8(payload.data)); + proxy_->dispatchPushEvent(request_id, data); } void ServiceWorkerContextClient::OnGeofencingEvent( @@ -800,23 +857,24 @@ void ServiceWorkerContextClient::OnPostMessage( base::TimeTicks::Now() - before); } -void ServiceWorkerContextClient::OnCrossOriginMessageToWorker( - const NavigatorConnectClient& client, - const base::string16& message, - const std::vector& sent_message_ports, - const std::vector& new_routing_ids) { - TRACE_EVENT0("ServiceWorker", - "ServiceWorkerContextClient::OnCrossOriginMessageToWorker"); - blink::WebMessagePortChannelArray ports = - WebMessagePortChannelImpl::CreatePorts( - sent_message_ports, new_routing_ids, - main_thread_task_runner_); - - blink::WebCrossOriginServiceWorkerClient web_client; - web_client.origin = client.origin; - web_client.targetURL = client.target_url; - web_client.clientID = client.message_port_id; - proxy_->dispatchCrossOriginMessageEvent(web_client, message, ports); +void ServiceWorkerContextClient::OnDidGetClient( + int request_id, + const ServiceWorkerClientInfo& client) { + TRACE_EVENT0("ServiceWorker", "ServiceWorkerContextClient::OnDidGetClient"); + blink::WebServiceWorkerClientCallbacks* callbacks = + context_->client_callbacks.Lookup(request_id); + if (!callbacks) { + NOTREACHED() << "Got stray response: " << request_id; + return; + } + scoped_ptr web_client; + if (!client.IsEmpty()) { + DCHECK(client.IsValid()); + web_client.reset(new blink::WebServiceWorkerClientInfo( + ToWebServiceWorkerClientInfo(client))); + } + callbacks->onSuccess(std::move(web_client)); + context_->client_callbacks.Remove(request_id); } void ServiceWorkerContextClient::OnDidGetClients( @@ -856,7 +914,7 @@ void ServiceWorkerContextClient::OnOpenWindowResponse( web_client.reset(new blink::WebServiceWorkerClientInfo( ToWebServiceWorkerClientInfo(client))); } - callbacks->onSuccess(adoptWebPtr(web_client.release())); + callbacks->onSuccess(std::move(web_client)); context_->client_callbacks.Remove(request_id); } @@ -892,7 +950,7 @@ void ServiceWorkerContextClient::OnFocusClientResponse( scoped_ptr web_client ( new blink::WebServiceWorkerClientInfo( ToWebServiceWorkerClientInfo(client))); - callback->onSuccess(adoptWebPtr(web_client.release())); + callback->onSuccess(std::move(web_client)); } else { callback->onError(blink::WebServiceWorkerError( blink::WebServiceWorkerError::ErrorTypeNotFound, @@ -919,7 +977,7 @@ void ServiceWorkerContextClient::OnNavigateClientResponse( web_client.reset(new blink::WebServiceWorkerClientInfo( ToWebServiceWorkerClientInfo(client))); } - callbacks->onSuccess(adoptWebPtr(web_client.release())); + callbacks->onSuccess(std::move(web_client)); context_->client_callbacks.Remove(request_id); } diff --git a/chromium/content/renderer/service_worker/service_worker_context_client.h b/chromium/content/renderer/service_worker/service_worker_context_client.h index 6356e37bef0..5cc4121d755 100644 --- a/chromium/content/renderer/service_worker/service_worker_context_client.h +++ b/chromium/content/renderer/service_worker/service_worker_context_client.h @@ -22,7 +22,7 @@ #include "content/common/service_worker/service_worker_types.h" #include "content/public/common/service_worker_event_status.mojom.h" #include "ipc/ipc_listener.h" -#include "mojo/shell/public/interfaces/service_provider.mojom.h" +#include "mojo/shell/public/interfaces/interface_provider.mojom.h" #include "third_party/WebKit/public/platform/WebGeofencingEventType.h" #include "third_party/WebKit/public/platform/WebMessagePortChannel.h" #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerError.h" @@ -30,6 +30,8 @@ #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextProxy.h" #include "v8/include/v8.h" +struct ServiceWorkerMsg_ExtendableMessageEvent_Params; + namespace base { class SingleThreadTaskRunner; class TaskRunner; @@ -37,7 +39,6 @@ class TaskRunner; namespace blink { struct WebCircularGeofencingRegion; -struct WebCrossOriginServiceWorkerClient; class WebDataSource; struct WebServiceWorkerClientQueryOptions; class WebServiceWorkerContextProxy; @@ -51,8 +52,8 @@ class Message; namespace content { -struct NavigatorConnectClient; struct PlatformNotificationData; +struct PushEventPayload; struct ServiceWorkerClientInfo; class ServiceWorkerProviderContext; class ServiceWorkerContextClient; @@ -64,7 +65,7 @@ class WebServiceWorkerRegistrationImpl; class ServiceWorkerContextClient : public blink::WebServiceWorkerContextClient { public: - using SyncCallback = mojo::Callback; + using SyncCallback = mojo::Callback; // Returns a thread-specific client instance. This does NOT create a // new instance. @@ -86,11 +87,13 @@ class ServiceWorkerContextClient // ServiceRegistry to connect to services before this method is called are // queued up and will resolve after this method is called. void BindServiceRegistry( - mojo::InterfaceRequest services, - mojo::ServiceProviderPtr exposed_services); + mojo::shell::mojom::InterfaceProviderRequest services, + mojo::shell::mojom::InterfaceProviderPtr exposed_services); // WebServiceWorkerContextClient overrides. blink::WebURL scope() const override; + void getClient(const blink::WebString&, + blink::WebServiceWorkerClientCallbacks*) override; void getClients(const blink::WebServiceWorkerClientQueryOptions&, blink::WebServiceWorkerClientsCallbacks*) override; void openWindow(const blink::WebURL&, @@ -108,8 +111,7 @@ class ServiceWorkerContextClient void workerContextStarted( blink::WebServiceWorkerContextProxy* proxy) override; void didEvaluateWorkerScript(bool success) override; - void didInitializeWorkerContext(v8::Local context, - const blink::WebURL& url) override; + void didInitializeWorkerContext(v8::Local context) override; void willDestroyWorkerContext(v8::Local context) override; void workerContextDestroyed() override; void reportException(const blink::WebString& error_message, @@ -127,6 +129,9 @@ class ServiceWorkerContextClient const blink::WebString& state) override; void didHandleActivateEvent(int request_id, blink::WebServiceWorkerEventResult) override; + void didHandleExtendableMessageEvent( + int request_id, + blink::WebServiceWorkerEventResult result) override; void didHandleInstallEvent( int request_id, blink::WebServiceWorkerEventResult result) override; @@ -137,6 +142,9 @@ class ServiceWorkerContextClient void didHandleNotificationClickEvent( int request_id, blink::WebServiceWorkerEventResult result) override; + void didHandleNotificationCloseEvent( + int request_id, + blink::WebServiceWorkerEventResult result) override; void didHandlePushEvent(int request_id, blink::WebServiceWorkerEventResult result) override; void didHandleSyncEvent(int request_id, @@ -152,9 +160,9 @@ class ServiceWorkerContextClient const blink::WebString& message, blink::WebMessagePortChannelArray* channels) override; void postMessageToCrossOriginClient( - const blink::WebCrossOriginServiceWorkerClient& client, - const blink::WebString& message, - blink::WebMessagePortChannelArray* channels) override; + const blink::WebCrossOriginServiceWorkerClient&, + const blink::WebString&, + blink::WebMessagePortChannelArray*) override; void focus(const blink::WebString& uuid, blink::WebServiceWorkerClientCallbacks*) override; void navigate(const blink::WebString& uuid, @@ -164,10 +172,11 @@ class ServiceWorkerContextClient blink::WebServiceWorkerSkipWaitingCallbacks* callbacks) override; void claim(blink::WebServiceWorkerClientsClaimCallbacks* callbacks) override; void registerForeignFetchScopes( - const blink::WebVector& sub_scopes) override; + const blink::WebVector& sub_scopes, + const blink::WebVector& origins) override; virtual void DispatchSyncEvent( - const blink::WebSyncRegistration& registration, + const std::string& tag, blink::WebServiceWorkerContextProxy::LastChanceOption last_chance, const SyncCallback& callback); @@ -185,6 +194,9 @@ class ServiceWorkerContextClient const ServiceWorkerVersionAttributes& attrs); void OnActivateEvent(int request_id); + void OnExtendableMessageEvent( + int request_id, + const ServiceWorkerMsg_ExtendableMessageEvent_Params& params); void OnInstallEvent(int request_id); void OnFetchEvent(int request_id, const ServiceWorkerFetchRequest& request); void OnNotificationClickEvent( @@ -192,20 +204,24 @@ class ServiceWorkerContextClient int64_t persistent_notification_id, const PlatformNotificationData& notification_data, int action_index); - void OnPushEvent(int request_id, const std::string& data); + void OnPushEvent(int request_id, const PushEventPayload& payload); + void OnNotificationCloseEvent( + int request_id, + int64_t persistent_notification_id, + const PlatformNotificationData& notification_data); void OnGeofencingEvent(int request_id, blink::WebGeofencingEventType event_type, const std::string& region_id, const blink::WebCircularGeofencingRegion& region); + + // TODO(nhiroki): Remove this after ExtendableMessageEvent is enabled by + // default (crbug.com/543198). void OnPostMessage( const base::string16& message, const std::vector& sent_message_ports, const std::vector& new_routing_ids); - void OnCrossOriginMessageToWorker( - const NavigatorConnectClient& client, - const base::string16& message, - const std::vector& sent_message_ports, - const std::vector& new_routing_ids); + + void OnDidGetClient(int request_id, const ServiceWorkerClientInfo& client); void OnDidGetClients( int request_id, const std::vector& clients); void OnOpenWindowResponse(int request_id, diff --git a/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.cc b/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.cc index 11463635f86..97b5063b8e7 100644 --- a/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.cc +++ b/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.cc @@ -27,18 +27,6 @@ EmbeddedSharedWorkerContentSettingsClientProxy:: ~EmbeddedSharedWorkerContentSettingsClientProxy() { } -bool EmbeddedSharedWorkerContentSettingsClientProxy::allowDatabase( - const blink::WebString& name, - const blink::WebString& display_name, - unsigned long estimated_size) { - if (is_unique_origin_) - return false; - bool result = false; - thread_safe_sender_->Send(new WorkerProcessHostMsg_AllowDatabase( - routing_id_, origin_url_, name, display_name, estimated_size, &result)); - return result; -} - bool EmbeddedSharedWorkerContentSettingsClientProxy::requestFileSystemAccessSync() { if (is_unique_origin_) diff --git a/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.h b/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.h index e87e30d3dd6..b93d989a6cf 100644 --- a/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.h +++ b/chromium/content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.h @@ -27,9 +27,6 @@ class EmbeddedSharedWorkerContentSettingsClientProxy ~EmbeddedSharedWorkerContentSettingsClientProxy() override; // WebWorkerContentSettingsClientProxy overrides. - bool allowDatabase(const blink::WebString& name, - const blink::WebString& display_name, - unsigned long estimated_size) override; bool requestFileSystemAccessSync() override; bool allowIndexedDB(const blink::WebString& name) override; diff --git a/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.cc index 39b0d3526e3..30ac9cb9ccf 100644 --- a/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.cc +++ b/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.cc @@ -21,9 +21,9 @@ #include "content/renderer/render_thread_impl.h" #include "content/renderer/shared_worker/embedded_shared_worker_content_settings_client_proxy.h" #include "ipc/ipc_message_macros.h" -#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/platform/URLConversion.h" +#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebDataSource.h" -#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebSharedWorker.h" #include "third_party/WebKit/public/web/WebSharedWorkerClient.h" #include "third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h" @@ -83,12 +83,12 @@ class WebServiceWorkerNetworkProviderImpl blink::WebURLRequest& request) override { ServiceWorkerNetworkProvider* provider = GetNetworkProviderFromDataSource(data_source); - scoped_ptr extra_data(new RequestExtraData); + std::unique_ptr extra_data(new RequestExtraData); extra_data->set_service_worker_provider_id(provider->provider_id()); request.setExtraData(extra_data.release()); // Explicitly set the SkipServiceWorker flag for subresources here if the // renderer process hasn't received SetControllerServiceWorker message. - if (request.requestContext() != + if (request.getRequestContext() != blink::WebURLRequest::RequestContextSharedWorker && !provider->IsControlledByServiceWorker()) { request.setSkipServiceWorker(true); @@ -113,7 +113,7 @@ class WebServiceWorkerNetworkProviderImpl ServiceWorkerNetworkProvider* GetNetworkProviderFromDataSource( const blink::WebDataSource* data_source) { return ServiceWorkerNetworkProvider::FromDocumentState( - static_cast(data_source->extraData())); + static_cast(data_source->getExtraData())); } }; @@ -124,11 +124,10 @@ EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub( const base::string16& name, const base::string16& content_security_policy, blink::WebContentSecurityPolicyType security_policy_type, + blink::WebAddressSpace creation_address_space, bool pause_on_start, int route_id) - : route_id_(route_id), - name_(name), - url_(url) { + : route_id_(route_id), name_(name), url_(url) { RenderThreadImpl::current()->AddEmbeddedWorkerRoute(route_id_, this); impl_ = blink::WebSharedWorker::create(this); if (pause_on_start) { @@ -138,8 +137,8 @@ EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub( } worker_devtools_agent_.reset( new SharedWorkerDevToolsAgent(route_id, impl_)); - impl_->startWorkerContext(url, name_, - content_security_policy, security_policy_type); + impl_->startWorkerContext(url, name_, content_security_policy, + security_policy_type, creation_address_space); } EmbeddedSharedWorkerStub::~EmbeddedSharedWorkerStub() { @@ -229,7 +228,7 @@ blink::WebWorkerContentSettingsClientProxy* EmbeddedSharedWorkerStub::createWorkerContentSettingsClientProxy( const blink::WebSecurityOrigin& origin) { return new EmbeddedSharedWorkerContentSettingsClientProxy( - GURL(origin.toString()), + blink::WebStringToGURL(origin.toString()), origin.isUnique(), route_id_, ChildThreadImpl::current()->thread_safe_sender()); @@ -240,7 +239,7 @@ EmbeddedSharedWorkerStub::createServiceWorkerNetworkProvider( blink::WebDataSource* data_source) { // Create a content::ServiceWorkerNetworkProvider for this data source so // we can observe its requests. - scoped_ptr provider( + std::unique_ptr provider( new ServiceWorkerNetworkProvider( route_id_, SERVICE_WORKER_PROVIDER_FOR_SHARED_WORKER)); diff --git a/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.h b/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.h index 232f55a7bfb..8442dd6df75 100644 --- a/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.h +++ b/chromium/content/renderer/shared_worker/embedded_shared_worker_stub.h @@ -5,12 +5,14 @@ #ifndef CONTENT_RENDERER_SHARED_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_ #define CONTENT_RENDERER_SHARED_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_ +#include #include #include "base/macros.h" #include "content/child/child_message_filter.h" #include "content/child/scoped_child_process_reference.h" #include "ipc/ipc_listener.h" +#include "third_party/WebKit/public/platform/WebAddressSpace.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebContentSecurityPolicy.h" #include "third_party/WebKit/public/web/WebSharedWorkerClient.h" @@ -49,6 +51,7 @@ class EmbeddedSharedWorkerStub : public IPC::Listener, const base::string16& name, const base::string16& content_security_policy, blink::WebContentSecurityPolicyType security_policy_type, + blink::WebAddressSpace creation_address_space, bool pause_on_start, int route_id); @@ -93,7 +96,7 @@ class EmbeddedSharedWorkerStub : public IPC::Listener, bool running_ = false; GURL url_; blink::WebSharedWorker* impl_ = nullptr; - scoped_ptr worker_devtools_agent_; + std::unique_ptr worker_devtools_agent_; typedef std::vector PendingChannelList; PendingChannelList pending_channels_; diff --git a/chromium/content/renderer/shared_worker_repository.cc b/chromium/content/renderer/shared_worker_repository.cc index 93b1a0647bc..607e044fe27 100644 --- a/chromium/content/renderer/shared_worker_repository.cc +++ b/chromium/content/renderer/shared_worker_repository.cc @@ -27,6 +27,7 @@ SharedWorkerRepository::createSharedWorkerConnector( DocumentID document_id, const blink::WebString& content_security_policy, blink::WebContentSecurityPolicyType security_policy_type, + blink::WebAddressSpace creation_address_space, blink::WebSharedWorkerCreationContextType creation_context_type, blink::WebWorkerCreationError* error) { ViewHostMsg_CreateWorker_Params params; @@ -36,6 +37,7 @@ SharedWorkerRepository::createSharedWorkerConnector( params.security_policy_type = security_policy_type; params.document_id = document_id; params.render_frame_route_id = render_frame()->GetRoutingID(); + params.creation_address_space = creation_address_space; params.creation_context_type = creation_context_type; ViewHostMsg_CreateWorker_Reply reply; Send(new ViewHostMsg_CreateWorker(params, &reply)); diff --git a/chromium/content/renderer/shared_worker_repository.h b/chromium/content/renderer/shared_worker_repository.h index 70d73d38f67..93ac628c379 100644 --- a/chromium/content/renderer/shared_worker_repository.h +++ b/chromium/content/renderer/shared_worker_repository.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "content/public/renderer/render_frame_observer.h" +#include "third_party/WebKit/public/platform/WebAddressSpace.h" #include "third_party/WebKit/public/web/WebContentSecurityPolicy.h" #include "third_party/WebKit/public/web/WebSharedWorkerCreationContextType.h" #include "third_party/WebKit/public/web/WebSharedWorkerRepositoryClient.h" @@ -30,6 +31,7 @@ class SharedWorkerRepository : public RenderFrameObserver, DocumentID document_id, const blink::WebString& content_security_policy, blink::WebContentSecurityPolicyType, + blink::WebAddressSpace, blink::WebSharedWorkerCreationContextType, blink::WebWorkerCreationError* error) override; void documentDetached(DocumentID document_id) override; diff --git a/chromium/content/renderer/skia_benchmarking_extension.cc b/chromium/content/renderer/skia_benchmarking_extension.cc index f5180d0fd42..45189302691 100644 --- a/chromium/content/renderer/skia_benchmarking_extension.cc +++ b/chromium/content/renderer/skia_benchmarking_extension.cc @@ -40,7 +40,7 @@ namespace { class Picture { public: gfx::Rect layer_rect; - skia::RefPtr picture; + sk_sp picture; }; bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) { @@ -70,13 +70,14 @@ scoped_ptr CreatePictureFromEncodedString(const std::string& encoded) { base::Base64Decode(encoded, &decoded); SkMemoryStream stream(decoded.data(), decoded.size()); - SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap); + sk_sp skpicture = + SkPicture::MakeFromStream(&stream, &DecodeBitmap); if (!skpicture) return nullptr; scoped_ptr picture(new Picture); picture->layer_rect = gfx::SkIRectToRect(skpicture->cullRect().roundOut()); - picture->picture = skia::AdoptRef(skpicture); + picture->picture = std::move(skpicture); return picture; } diff --git a/chromium/content/renderer/text_input_client_observer.cc b/chromium/content/renderer/text_input_client_observer.cc index eda58f206f1..b97be4a31bd 100644 --- a/chromium/content/renderer/text_input_client_observer.cc +++ b/chromium/content/renderer/text_input_client_observer.cc @@ -65,7 +65,8 @@ void TextInputClientObserver::OnStringAtPoint(gfx::Point point) { void TextInputClientObserver::OnCharacterIndexForPoint(gfx::Point point) { blink::WebPoint web_point(point); - size_t index = webview()->focusedFrame()->characterIndexForPoint(web_point); + uint32_t index = static_cast( + webview()->focusedFrame()->characterIndexForPoint(web_point)); Send(new TextInputClientReplyMsg_GotCharacterIndexForPoint(routing_id(), index)); } diff --git a/chromium/content/renderer/top_level_blame_context.cc b/chromium/content/renderer/top_level_blame_context.cc new file mode 100644 index 00000000000..6b8311a5852 --- /dev/null +++ b/chromium/content/renderer/top_level_blame_context.cc @@ -0,0 +1,25 @@ +// 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 "content/renderer/top_level_blame_context.h" + +#include "base/threading/platform_thread.h" +#include "content/renderer/frame_blame_context.h" + +namespace content { + +const char kTopLevelBlameContextCategory[] = "blink"; +const char kTopLevelBlameContextName[] = "FrameBlameContext"; +const char kTopLevelBlameContextType[] = "TopLevel"; +const char kTopLevelBlameContextScope[] = "PlatformThread"; + +TopLevelBlameContext::TopLevelBlameContext() + : base::trace_event::BlameContext(kTopLevelBlameContextCategory, + kTopLevelBlameContextName, + kTopLevelBlameContextType, + kTopLevelBlameContextScope, + base::PlatformThread::CurrentId(), + nullptr) {} + +} // namespace content diff --git a/chromium/content/renderer/top_level_blame_context.h b/chromium/content/renderer/top_level_blame_context.h new file mode 100644 index 00000000000..1138373e2cc --- /dev/null +++ b/chromium/content/renderer/top_level_blame_context.h @@ -0,0 +1,25 @@ +// 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 CONTENT_RENDERER_TOP_LEVEL_BLAME_CONTEXT_H_ +#define CONTENT_RENDERER_TOP_LEVEL_BLAME_CONTEXT_H_ + +#include "base/trace_event/blame_context.h" + +namespace content { + +// A blame context which spans all the frames in this renderer. Used for +// attributing work which cannot be associated with a specific frame (e.g., +// garbage collection). +class TopLevelBlameContext : public base::trace_event::BlameContext { + public: + TopLevelBlameContext(); + + private: + DISALLOW_COPY_AND_ASSIGN(TopLevelBlameContext); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_TOP_LEVEL_BLAME_CONTEXT_H_ diff --git a/chromium/content/renderer/usb/DEPS b/chromium/content/renderer/usb/DEPS index ddfef5f0857..b7aeef531c4 100644 --- a/chromium/content/renderer/usb/DEPS +++ b/chromium/content/renderer/usb/DEPS @@ -1,4 +1,3 @@ include_rules = [ - "+components/webusb/public/interfaces", - "+device/devices_app/usb/public", + "+device/usb/public", ] diff --git a/chromium/content/renderer/usb/type_converters.cc b/chromium/content/renderer/usb/type_converters.cc index 62c9aab163b..5c47c934662 100644 --- a/chromium/content/renderer/usb/type_converters.cc +++ b/chromium/content/renderer/usb/type_converters.cc @@ -16,9 +16,9 @@ TypeConverter:: Convert(const device::usb::TransferDirection& direction) { switch (direction) { - case device::usb::TRANSFER_DIRECTION_IN: + case device::usb::TransferDirection::INBOUND: return blink::WebUSBDevice::TransferDirection::In; - case device::usb::TRANSFER_DIRECTION_OUT: + case device::usb::TransferDirection::OUTBOUND: return blink::WebUSBDevice::TransferDirection::Out; default: NOTREACHED(); @@ -33,12 +33,12 @@ TypeConverter::Convert(const device::usb::EndpointType& endpoint_type) { switch (endpoint_type) { - case device::usb::ENDPOINT_TYPE_BULK: + case device::usb::EndpointType::BULK: return blink::WebUSBDeviceInfo::Endpoint::Type::Bulk; - case device::usb::ENDPOINT_TYPE_INTERRUPT: + case device::usb::EndpointType::INTERRUPT: return blink::WebUSBDeviceInfo::Endpoint::Type::Interrupt; - case device::usb::ENDPOINT_TYPE_ISOCHRONOUS: + case device::usb::EndpointType::ISOCHRONOUS: return blink::WebUSBDeviceInfo::Endpoint::Type::Isochronous; default: NOTREACHED(); @@ -213,6 +213,7 @@ TypeConverter::Convert( device.productName = blink::WebString::fromUTF8(info->product_name); if (!info->serial_number.is_null()) device.serialNumber = blink::WebString::fromUTF8(info->serial_number); + device.activeConfiguration = info->active_configuration; device.configurations = blink::WebVector( info->configurations.size()); diff --git a/chromium/content/renderer/usb/type_converters.h b/chromium/content/renderer/usb/type_converters.h index 0ec030a95f7..3bdec856996 100644 --- a/chromium/content/renderer/usb/type_converters.h +++ b/chromium/content/renderer/usb/type_converters.h @@ -4,8 +4,8 @@ #include "mojo/public/cpp/bindings/type_converter.h" -#include "device/devices_app/usb/public/interfaces/device.mojom.h" -#include "device/devices_app/usb/public/interfaces/device_manager.mojom.h" +#include "device/usb/public/interfaces/device.mojom.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDevice.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceFilter.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceInfo.h" diff --git a/chromium/content/renderer/usb/web_usb_client_impl.cc b/chromium/content/renderer/usb/web_usb_client_impl.cc index ed9246a0aef..fc79424dfe3 100644 --- a/chromium/content/renderer/usb/web_usb_client_impl.cc +++ b/chromium/content/renderer/usb/web_usb_client_impl.cc @@ -5,10 +5,13 @@ #include "content/renderer/usb/web_usb_client_impl.h" #include + +#include #include #include "base/bind.h" #include "base/callback.h" +#include "base/memory/ptr_util.h" #include "base/memory/scoped_ptr.h" #include "base/move.h" #include "base/strings/utf_string_conversions.h" @@ -20,7 +23,6 @@ #include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "third_party/WebKit/public/platform/WebCallbacks.h" -#include "third_party/WebKit/public/platform/WebPassOwnPtr.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceFilter.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceInfo.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceRequestOptions.h" @@ -57,8 +59,9 @@ void OnGetDevicesComplete( ScopedWebCallbacks scoped_callbacks, device::usb::DeviceManager* device_manager, mojo::Array results) { - blink::WebVector* devices = - new blink::WebVector(results.size()); + // TODO(dcheng): This WebVector should hold smart pointers. + std::unique_ptr> devices( + new blink::WebVector(results.size())); for (size_t i = 0; i < results.size(); ++i) { device::usb::DevicePtr device; device_manager->GetDevice(results[i]->guid, mojo::GetProxy(&device)); @@ -66,7 +69,7 @@ void OnGetDevicesComplete( std::move(device), mojo::ConvertTo(results[i])); } - scoped_callbacks.PassCallbacks()->onSuccess(blink::adoptWebPtr(devices)); + scoped_callbacks.PassCallbacks()->onSuccess(std::move(devices)); } void OnRequestDevicesComplete( @@ -77,10 +80,10 @@ void OnRequestDevicesComplete( if (result) { device::usb::DevicePtr device; device_manager->GetDevice(result->guid, mojo::GetProxy(&device)); - blink::WebUSBDevice* web_usb_device = new WebUSBDeviceImpl( - std::move(device), mojo::ConvertTo(result)); + std::unique_ptr web_usb_device(new WebUSBDeviceImpl( + std::move(device), mojo::ConvertTo(result))); - scoped_callbacks->onSuccess(blink::adoptWebPtr(web_usb_device)); + scoped_callbacks->onSuccess(std::move(web_usb_device)); } else { scoped_callbacks->onError( blink::WebUSBError(blink::WebUSBError::Error::NotFound, @@ -107,9 +110,9 @@ void WebUSBClientImpl::getDevices( void WebUSBClientImpl::requestDevice( const blink::WebUSBDeviceRequestOptions& options, blink::WebUSBClientRequestDeviceCallbacks* callbacks) { - if (!webusb_permission_bubble_) { + if (!chooser_service_) { service_registry_->ConnectToRemoteService( - mojo::GetProxy(&webusb_permission_bubble_)); + mojo::GetProxy(&chooser_service_)); } auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); @@ -117,14 +120,14 @@ void WebUSBClientImpl::requestDevice( mojo::Array device_filters = mojo::Array::From(options.filters); - webusb_permission_bubble_->GetPermission( + chooser_service_->GetPermission( std::move(device_filters), base::Bind(&OnRequestDevicesComplete, base::Passed(&scoped_callbacks), base::Unretained(device_manager_.get()))); } -void WebUSBClientImpl::setObserver(Observer* observer) { - if (!observer_) { +void WebUSBClientImpl::addObserver(Observer* observer) { + if (observers_.empty()) { // Set up two sequential calls to GetDeviceChanges to avoid latency. device::usb::DeviceManager* device_manager = GetDeviceManager(); device_manager->GetDeviceChanges(base::Bind( @@ -133,7 +136,12 @@ void WebUSBClientImpl::setObserver(Observer* observer) { &WebUSBClientImpl::OnDeviceChangeNotification, base::Unretained(this))); } - observer_ = observer; + observers_.insert(observer); +} + +void WebUSBClientImpl::removeObserver(Observer* observer) { + DCHECK(ContainsKey(observers_, observer)); + observers_.erase(observer); } device::usb::DeviceManager* WebUSBClientImpl::GetDeviceManager() { @@ -144,7 +152,7 @@ device::usb::DeviceManager* WebUSBClientImpl::GetDeviceManager() { void WebUSBClientImpl::OnDeviceChangeNotification( device::usb::DeviceChangeNotificationPtr notification) { - if (!observer_) + if (observers_.empty()) return; device_manager_->GetDeviceChanges(base::Bind( @@ -152,20 +160,20 @@ void WebUSBClientImpl::OnDeviceChangeNotification( for (size_t i = 0; i < notification->devices_added.size(); ++i) { const device::usb::DeviceInfoPtr& device_info = notification->devices_added[i]; - device::usb::DevicePtr device; - device_manager_->GetDevice(device_info->guid, mojo::GetProxy(&device)); - observer_->onDeviceConnected(blink::adoptWebPtr(new WebUSBDeviceImpl( - std::move(device), - mojo::ConvertTo(device_info)))); + for (auto observer : observers_) { + device::usb::DevicePtr device; + device_manager_->GetDevice(device_info->guid, mojo::GetProxy(&device)); + observer->onDeviceConnected(base::WrapUnique(new WebUSBDeviceImpl( + std::move(device), + mojo::ConvertTo(device_info)))); + } } for (size_t i = 0; i < notification->devices_removed.size(); ++i) { const device::usb::DeviceInfoPtr& device_info = notification->devices_removed[i]; - device::usb::DevicePtr device; - device_manager_->GetDevice(device_info->guid, mojo::GetProxy(&device)); - observer_->onDeviceDisconnected(blink::adoptWebPtr(new WebUSBDeviceImpl( - std::move(device), - mojo::ConvertTo(device_info)))); + for (auto observer : observers_) + observer->onDeviceDisconnected(base::WrapUnique(new WebUSBDeviceImpl( + nullptr, mojo::ConvertTo(device_info)))); } } diff --git a/chromium/content/renderer/usb/web_usb_client_impl.h b/chromium/content/renderer/usb/web_usb_client_impl.h index fddd809d8dd..10fe314447e 100644 --- a/chromium/content/renderer/usb/web_usb_client_impl.h +++ b/chromium/content/renderer/usb/web_usb_client_impl.h @@ -6,8 +6,8 @@ #define CONTENT_RENDERER_USB_WEB_USB_CLIENT_IMPL_H_ #include "base/macros.h" -#include "components/webusb/public/interfaces/webusb_permission_bubble.mojom.h" -#include "device/devices_app/usb/public/interfaces/device_manager.mojom.h" +#include "device/usb/public/interfaces/chooser_service.mojom.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBClient.h" namespace content { @@ -25,7 +25,8 @@ class WebUSBClientImpl : public blink::WebUSBClient { void requestDevice( const blink::WebUSBDeviceRequestOptions& options, blink::WebUSBClientRequestDeviceCallbacks* callbacks) override; - void setObserver(Observer* observer) override; + void addObserver(Observer* observer) override; + void removeObserver(Observer* observer) override; device::usb::DeviceManager* GetDeviceManager(); void OnDeviceChangeNotification( @@ -33,8 +34,8 @@ class WebUSBClientImpl : public blink::WebUSBClient { ServiceRegistry* const service_registry_; device::usb::DeviceManagerPtr device_manager_; - webusb::WebUsbPermissionBubblePtr webusb_permission_bubble_; - Observer* observer_ = nullptr; + device::usb::ChooserServicePtr chooser_service_; + std::set observers_; DISALLOW_COPY_AND_ASSIGN(WebUSBClientImpl); }; diff --git a/chromium/content/renderer/usb/web_usb_device_impl.cc b/chromium/content/renderer/usb/web_usb_device_impl.cc index d1db890274a..bb2ca99c194 100644 --- a/chromium/content/renderer/usb/web_usb_device_impl.cc +++ b/chromium/content/renderer/usb/web_usb_device_impl.cc @@ -9,11 +9,11 @@ #include "base/bind.h" #include "base/callback.h" #include "base/strings/utf_string_conversions.h" +#include "content/child/mojo/type_converters.h" #include "content/child/scoped_web_callbacks.h" #include "content/renderer/usb/type_converters.h" -#include "device/devices_app/public/cpp/constants.h" #include "mojo/shell/public/cpp/connect.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" +#include "mojo/shell/public/interfaces/connector.mojom.h" #include "third_party/WebKit/public/platform/WebVector.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceInfo.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBTransferInfo.h" @@ -24,8 +24,8 @@ namespace { const char kClaimInterfaceFailed[] = "Unable to claim interface."; const char kClearHaltFailed[] = "Unable to clear endpoint."; +const char kDeviceAlreadyOpen[] = "Device has already been opened."; const char kDeviceNoAccess[] = "Access denied."; -const char kDeviceNotConfigured[] = "Device not configured."; const char kDeviceUnavailable[] = "Device unavailable."; const char kDeviceResetFailed[] = "Unable to reset the device."; const char kReleaseInterfaceFailed[] = "Unable to release interface."; @@ -66,14 +66,19 @@ void OnOpenDevice( device::usb::OpenDeviceError error) { auto scoped_callbacks = callbacks.PassCallbacks(); switch(error) { - case device::usb::OPEN_DEVICE_ERROR_OK: + case device::usb::OpenDeviceError::OK: scoped_callbacks->onSuccess(); break; - case device::usb::OPEN_DEVICE_ERROR_ACCESS_DENIED: + case device::usb::OpenDeviceError::ACCESS_DENIED: scoped_callbacks->onError(blink::WebUSBError( blink::WebUSBError::Error::Security, base::ASCIIToUTF16(kDeviceNoAccess))); break; + case device::usb::OpenDeviceError::ALREADY_OPEN: + scoped_callbacks->onError(blink::WebUSBError( + blink::WebUSBError::Error::InvalidState, + base::ASCIIToUTF16(kDeviceAlreadyOpen))); + break; default: NOTREACHED(); } @@ -84,19 +89,6 @@ void OnDeviceClosed( callbacks.PassCallbacks()->onSuccess(); } -void OnGetConfiguration( - ScopedWebCallbacks callbacks, - uint8_t configuration_value) { - auto scoped_callbacks = callbacks.PassCallbacks(); - if (configuration_value == 0) { - RejectWithError(blink::WebUSBError(blink::WebUSBError::Error::NotFound, - kDeviceNotConfigured), - std::move(scoped_callbacks)); - } else { - scoped_callbacks->onSuccess(configuration_value); - } -} - void HandlePassFailDeviceOperation( ScopedWebCallbacks> callbacks, @@ -113,45 +105,113 @@ void HandlePassFailDeviceOperation( } void OnTransferIn( - ScopedWebCallbacks callbacks, + ScopedWebCallbacks callbacks, device::usb::TransferStatus status, mojo::Array data) { auto scoped_callbacks = callbacks.PassCallbacks(); - if (status != device::usb::TRANSFER_STATUS_COMPLETED) { - RejectWithTransferError(std::move(scoped_callbacks)); - return; + blink::WebUSBTransferInfo::Status web_status; + switch (status) { + case device::usb::TransferStatus::COMPLETED: + web_status = blink::WebUSBTransferInfo::Status::Ok; + break; + case device::usb::TransferStatus::STALLED: + web_status = blink::WebUSBTransferInfo::Status::Stall; + break; + case device::usb::TransferStatus::BABBLE: + web_status = blink::WebUSBTransferInfo::Status::Babble; + break; + default: + RejectWithTransferError(std::move(scoped_callbacks)); + return; } scoped_ptr info(new blink::WebUSBTransferInfo()); - info->status = blink::WebUSBTransferInfo::Status::Ok; + info->status.assign( + std::vector(1, web_status)); info->data.assign(data); - scoped_callbacks->onSuccess(adoptWebPtr(info.release())); + scoped_callbacks->onSuccess(std::move(info)); } void OnTransferOut( - ScopedWebCallbacks callbacks, + ScopedWebCallbacks callbacks, size_t bytes_written, device::usb::TransferStatus status) { auto scoped_callbacks = callbacks.PassCallbacks(); - scoped_ptr info(new blink::WebUSBTransferInfo()); + blink::WebUSBTransferInfo::Status web_status; switch (status) { - case device::usb::TRANSFER_STATUS_COMPLETED: - info->status = blink::WebUSBTransferInfo::Status::Ok; + case device::usb::TransferStatus::COMPLETED: + web_status = blink::WebUSBTransferInfo::Status::Ok; break; - case device::usb::TRANSFER_STATUS_STALLED: - info->status = blink::WebUSBTransferInfo::Status::Stall; - break; - case device::usb::TRANSFER_STATUS_BABBLE: - info->status = blink::WebUSBTransferInfo::Status::Babble; + case device::usb::TransferStatus::STALLED: + web_status = blink::WebUSBTransferInfo::Status::Stall; break; default: RejectWithTransferError(std::move(scoped_callbacks)); return; } - // TODO(rockot): Device::ControlTransferOut should expose the number of bytes // actually transferred so we can send it from here. - info->bytesWritten = bytes_written; - scoped_callbacks->onSuccess(adoptWebPtr(info.release())); + scoped_ptr info(new blink::WebUSBTransferInfo()); + info->status.assign( + std::vector(1, web_status)); + info->bytesTransferred.assign(std::vector(1, bytes_written)); + scoped_callbacks->onSuccess(std::move(info)); +} + +void OnIsochronousTransferIn( + ScopedWebCallbacks callbacks, + mojo::Array data, + mojo::Array packets) { + auto scoped_callbacks = callbacks.PassCallbacks(); + scoped_ptr info(new blink::WebUSBTransferInfo()); + info->data.assign(data); + info->status = + blink::WebVector(packets.size()); + info->packetLength = blink::WebVector(packets.size()); + info->bytesTransferred = blink::WebVector(packets.size()); + for (size_t i = 0; i < packets.size(); ++i) { + switch (packets[i]->status) { + case device::usb::TransferStatus::COMPLETED: + info->status[i] = blink::WebUSBTransferInfo::Status::Ok; + break; + case device::usb::TransferStatus::STALLED: + info->status[i] = blink::WebUSBTransferInfo::Status::Stall; + break; + case device::usb::TransferStatus::BABBLE: + info->status[i] = blink::WebUSBTransferInfo::Status::Babble; + break; + default: + RejectWithTransferError(std::move(scoped_callbacks)); + return; + } + info->packetLength[i] = packets[i]->length; + info->bytesTransferred[i] = packets[i]->transferred_length; + } + scoped_callbacks->onSuccess(std::move(info)); +} + +void OnIsochronousTransferOut( + ScopedWebCallbacks callbacks, + mojo::Array packets) { + auto scoped_callbacks = callbacks.PassCallbacks(); + scoped_ptr info(new blink::WebUSBTransferInfo()); + info->status = + blink::WebVector(packets.size()); + info->bytesTransferred = blink::WebVector(packets.size()); + for (size_t i = 0; i < packets.size(); ++i) { + switch (packets[i]->status) { + case device::usb::TransferStatus::COMPLETED: + info->status[i] = blink::WebUSBTransferInfo::Status::Ok; + break; + case device::usb::TransferStatus::STALLED: + info->status[i] = blink::WebUSBTransferInfo::Status::Stall; + break; + default: + RejectWithTransferError(std::move(scoped_callbacks)); + return; + } + info->bytesTransferred[i] = packets[i]->transferred_length; + } + scoped_callbacks->onSuccess(std::move(info)); } } // namespace @@ -160,7 +220,10 @@ WebUSBDeviceImpl::WebUSBDeviceImpl(device::usb::DevicePtr device, const blink::WebUSBDeviceInfo& device_info) : device_(std::move(device)), device_info_(device_info), - weak_factory_(this) {} + weak_factory_(this) { + if (device_) + device_.set_connection_error_handler([this]() { device_.reset(); }); +} WebUSBDeviceImpl::~WebUSBDeviceImpl() {} @@ -170,49 +233,48 @@ const blink::WebUSBDeviceInfo& WebUSBDeviceImpl::info() const { void WebUSBDeviceImpl::open(blink::WebUSBDeviceOpenCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->Open(base::Bind(&OnOpenDevice, base::Passed(&scoped_callbacks))); + if (device_) + device_->Open(base::Bind(&OnOpenDevice, base::Passed(&scoped_callbacks))); } void WebUSBDeviceImpl::close(blink::WebUSBDeviceCloseCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->Close(base::Bind(&OnDeviceClosed, base::Passed(&scoped_callbacks))); -} - -void WebUSBDeviceImpl::getConfiguration( - blink::WebUSBDeviceGetConfigurationCallbacks* callbacks) { - auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->GetConfiguration( - base::Bind(&OnGetConfiguration, base::Passed(&scoped_callbacks))); + if (device_) + device_->Close( + base::Bind(&OnDeviceClosed, base::Passed(&scoped_callbacks))); } void WebUSBDeviceImpl::setConfiguration( uint8_t configuration_value, blink::WebUSBDeviceSetConfigurationCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->SetConfiguration( - configuration_value, - base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), kSetConfigurationFailed)); + if (device_) + device_->SetConfiguration( + configuration_value, + base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), kSetConfigurationFailed)); } void WebUSBDeviceImpl::claimInterface( uint8_t interface_number, blink::WebUSBDeviceClaimInterfaceCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->ClaimInterface( - interface_number, - base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), kClaimInterfaceFailed)); + if (device_) + device_->ClaimInterface( + interface_number, + base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), kClaimInterfaceFailed)); } void WebUSBDeviceImpl::releaseInterface( uint8_t interface_number, blink::WebUSBDeviceReleaseInterfaceCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->ReleaseInterface( - interface_number, - base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), kReleaseInterfaceFailed)); + if (device_) + device_->ReleaseInterface( + interface_number, + base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), kReleaseInterfaceFailed)); } void WebUSBDeviceImpl::setInterface( @@ -220,20 +282,22 @@ void WebUSBDeviceImpl::setInterface( uint8_t alternate_setting, blink::WebUSBDeviceSetInterfaceAlternateSettingCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->SetInterfaceAlternateSetting( - interface_number, alternate_setting, - base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), kSetInterfaceFailed)); + if (device_) + device_->SetInterfaceAlternateSetting( + interface_number, alternate_setting, + base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), kSetInterfaceFailed)); } void WebUSBDeviceImpl::clearHalt( uint8_t endpoint_number, blink::WebUSBDeviceClearHaltCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->ClearHalt( - endpoint_number, - base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), kClearHaltFailed)); + if (device_) + device_->ClearHalt( + endpoint_number, + base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), kClearHaltFailed)); } void WebUSBDeviceImpl::controlTransfer( @@ -241,8 +305,11 @@ void WebUSBDeviceImpl::controlTransfer( uint8_t* data, size_t data_size, unsigned int timeout, - blink::WebUSBDeviceControlTransferCallbacks* callbacks) { + blink::WebUSBDeviceTransferCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); + if (!device_) + return; + device::usb::ControlTransferParamsPtr params = device::usb::ControlTransferParams::From(parameters); switch (parameters.direction) { @@ -274,8 +341,11 @@ void WebUSBDeviceImpl::transfer( uint8_t* data, size_t data_size, unsigned int timeout, - blink::WebUSBDeviceBulkTransferCallbacks* callbacks) { + blink::WebUSBDeviceTransferCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); + if (!device_) + return; + switch (direction) { case WebUSBDevice::TransferDirection::In: device_->GenericTransferIn( @@ -299,11 +369,49 @@ void WebUSBDeviceImpl::transfer( } } +void WebUSBDeviceImpl::isochronousTransfer( + blink::WebUSBDevice::TransferDirection direction, + uint8_t endpoint_number, + uint8_t* data, + size_t data_size, + blink::WebVector packet_lengths, + unsigned int timeout, + blink::WebUSBDeviceTransferCallbacks* callbacks) { + auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); + if (!device_) + return; + + switch (direction) { + case WebUSBDevice::TransferDirection::In: + device_->IsochronousTransferIn( + endpoint_number, mojo::Array::From(packet_lengths), timeout, + base::Bind(&OnIsochronousTransferIn, + base::Passed(&scoped_callbacks))); + break; + case WebUSBDevice::TransferDirection::Out: { + std::vector bytes; + if (data) + bytes.assign(data, data + data_size); + mojo::Array mojo_bytes; + mojo_bytes.Swap(&bytes); + device_->IsochronousTransferOut( + endpoint_number, std::move(mojo_bytes), + mojo::Array::From(packet_lengths), timeout, + base::Bind(&OnIsochronousTransferOut, + base::Passed(&scoped_callbacks))); + break; + } + default: + NOTREACHED(); + } +} + void WebUSBDeviceImpl::reset(blink::WebUSBDeviceResetCallbacks* callbacks) { auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks); - device_->Reset(base::Bind(&HandlePassFailDeviceOperation, - base::Passed(&scoped_callbacks), - kDeviceResetFailed)); + if (device_) + device_->Reset(base::Bind(&HandlePassFailDeviceOperation, + base::Passed(&scoped_callbacks), + kDeviceResetFailed)); } } // namespace content diff --git a/chromium/content/renderer/usb/web_usb_device_impl.h b/chromium/content/renderer/usb/web_usb_device_impl.h index dd2e9a211ef..dea2e8f24af 100644 --- a/chromium/content/renderer/usb/web_usb_device_impl.h +++ b/chromium/content/renderer/usb/web_usb_device_impl.h @@ -11,9 +11,9 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "device/devices_app/usb/public/interfaces/device.mojom.h" -#include "device/devices_app/usb/public/interfaces/device_manager.mojom.h" -#include "mojo/shell/public/interfaces/service_provider.mojom.h" +#include "device/usb/public/interfaces/device.mojom.h" +#include "device/usb/public/interfaces/device_manager.mojom.h" +#include "mojo/shell/public/interfaces/interface_provider.mojom.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDevice.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceInfo.h" #include "third_party/WebKit/public/platform/modules/webusb/WebUSBError.h" @@ -35,8 +35,6 @@ class WebUSBDeviceImpl : public blink::WebUSBDevice { const blink::WebUSBDeviceInfo& info() const override; void open(blink::WebUSBDeviceOpenCallbacks* callbacks) override; void close(blink::WebUSBDeviceCloseCallbacks* callbacks) override; - void getConfiguration( - blink::WebUSBDeviceGetConfigurationCallbacks* callbacks) override; void setConfiguration( uint8_t configuration_value, blink::WebUSBDeviceSetConfigurationCallbacks* callbacks) override; @@ -57,13 +55,21 @@ class WebUSBDeviceImpl : public blink::WebUSBDevice { uint8_t* data, size_t data_size, unsigned int timeout, - blink::WebUSBDeviceControlTransferCallbacks* callbacks) override; + blink::WebUSBDeviceTransferCallbacks* callbacks) override; void transfer(blink::WebUSBDevice::TransferDirection direction, uint8_t endpoint_number, uint8_t* data, size_t data_size, unsigned int timeout, - blink::WebUSBDeviceBulkTransferCallbacks* callbacks) override; + blink::WebUSBDeviceTransferCallbacks* callbacks) override; + void isochronousTransfer( + blink::WebUSBDevice::TransferDirection direction, + uint8_t endpoint_number, + uint8_t* data, + size_t data_size, + blink::WebVector packet_lengths, + unsigned int timeout, + blink::WebUSBDeviceTransferCallbacks* callbacks) override; void reset(blink::WebUSBDeviceResetCallbacks* callbacks) override; device::usb::DevicePtr device_; diff --git a/chromium/content/renderer/vr/vr_dispatcher.cc b/chromium/content/renderer/vr/vr_dispatcher.cc index 07391761338..acd893747f3 100644 --- a/chromium/content/renderer/vr/vr_dispatcher.cc +++ b/chromium/content/renderer/vr/vr_dispatcher.cc @@ -18,7 +18,7 @@ VRDispatcher::VRDispatcher(ServiceRegistry* service_registry) VRDispatcher::~VRDispatcher() { } -VRServicePtr& VRDispatcher::GetVRServicePtr() { +mojom::VRServicePtr& VRDispatcher::GetVRServicePtr() { if (!vr_service_) { service_registry_->ConnectToRemoteService(mojo::GetProxy(&vr_service_)); } @@ -46,8 +46,9 @@ void VRDispatcher::resetSensor(unsigned int index) { GetVRServicePtr()->ResetSensor(index); } -void VRDispatcher::OnGetDevices(int request_id, - const mojo::Array& devices) { +void VRDispatcher::OnGetDevices( + int request_id, + const mojo::Array& devices) { blink::WebVector web_devices(devices.size()); blink::WebVRGetDevicesCallback* callback = @@ -64,7 +65,7 @@ void VRDispatcher::OnGetDevices(int request_id, } void VRDispatcher::OnGetSensorState(blink::WebHMDSensorState* state, - const VRSensorStatePtr& mojo_state) { + const mojom::VRSensorStatePtr& mojo_state) { *state = mojo_state.To(); } diff --git a/chromium/content/renderer/vr/vr_dispatcher.h b/chromium/content/renderer/vr/vr_dispatcher.h index b027d05be74..7e2f422a051 100644 --- a/chromium/content/renderer/vr/vr_dispatcher.h +++ b/chromium/content/renderer/vr/vr_dispatcher.h @@ -33,20 +33,20 @@ class VRDispatcher : NON_EXPORTED_BASE(public blink::WebVRClient) { private: // Helper method that returns an initialized PermissionServicePtr. - VRServicePtr& GetVRServicePtr(); + mojom::VRServicePtr& GetVRServicePtr(); // Callback handlers void OnGetDevices(int request_id, - const mojo::Array& devices); + const mojo::Array& devices); static void OnGetSensorState(blink::WebHMDSensorState* state, - const VRSensorStatePtr& mojo_state); + const mojom::VRSensorStatePtr& mojo_state); // Tracks requests sent to browser to match replies with callbacks. // Owns callback objects. IDMap pending_requests_; ServiceRegistry* service_registry_; - VRServicePtr vr_service_; + mojom::VRServicePtr vr_service_; DISALLOW_COPY_AND_ASSIGN(VRDispatcher); }; diff --git a/chromium/content/renderer/vr/vr_type_converters.cc b/chromium/content/renderer/vr/vr_type_converters.cc index ba220a4f4f9..61b14d1e9fa 100644 --- a/chromium/content/renderer/vr/vr_type_converters.cc +++ b/chromium/content/renderer/vr/vr_type_converters.cc @@ -8,14 +8,14 @@ #include -using content::VRVector3Ptr; -using content::VRVector4Ptr; -using content::VRRectPtr; -using content::VRFieldOfViewPtr; -using content::VREyeParametersPtr; -using content::VRHMDInfoPtr; -using content::VRDeviceInfoPtr; -using content::VRSensorStatePtr; +using content::mojom::VRVector3Ptr; +using content::mojom::VRVector4Ptr; +using content::mojom::VRRectPtr; +using content::mojom::VRFieldOfViewPtr; +using content::mojom::VREyeParametersPtr; +using content::mojom::VRHMDInfoPtr; +using content::mojom::VRDeviceInfoPtr; +using content::mojom::VRSensorStatePtr; namespace mojo { diff --git a/chromium/content/renderer/vr/vr_type_converters.h b/chromium/content/renderer/vr/vr_type_converters.h index 8902cd5a844..86b874abb2d 100644 --- a/chromium/content/renderer/vr/vr_type_converters.h +++ b/chromium/content/renderer/vr/vr_type_converters.h @@ -15,46 +15,50 @@ namespace mojo { // and vice versa. template <> -struct TypeConverter { - static blink::WebVRVector3 Convert(const content::VRVector3Ptr& input); +struct TypeConverter { + static blink::WebVRVector3 Convert(const content::mojom::VRVector3Ptr& input); }; template <> -struct TypeConverter { - static blink::WebVRVector4 Convert(const content::VRVector4Ptr& input); +struct TypeConverter { + static blink::WebVRVector4 Convert(const content::mojom::VRVector4Ptr& input); }; template <> -struct TypeConverter { - static blink::WebVRRect Convert(const content::VRRectPtr& input); +struct TypeConverter { + static blink::WebVRRect Convert(const content::mojom::VRRectPtr& input); }; template <> -struct TypeConverter { +struct TypeConverter { static blink::WebVRFieldOfView Convert( - const content::VRFieldOfViewPtr& input); + const content::mojom::VRFieldOfViewPtr& input); }; template <> -struct TypeConverter { +struct TypeConverter { static blink::WebVREyeParameters Convert( - const content::VREyeParametersPtr& input); + const content::mojom::VREyeParametersPtr& input); }; template <> -struct TypeConverter { - static blink::WebVRHMDInfo Convert(const content::VRHMDInfoPtr& input); +struct TypeConverter { + static blink::WebVRHMDInfo Convert(const content::mojom::VRHMDInfoPtr& input); }; template <> -struct TypeConverter { - static blink::WebVRDevice Convert(const content::VRDeviceInfoPtr& input); +struct TypeConverter { + static blink::WebVRDevice Convert( + const content::mojom::VRDeviceInfoPtr& input); }; template <> -struct TypeConverter { +struct TypeConverter { static blink::WebHMDSensorState Convert( - const content::VRSensorStatePtr& input); + const content::mojom::VRSensorStatePtr& input); }; } // namespace mojo diff --git a/chromium/content/renderer/wake_lock/wake_lock_dispatcher.h b/chromium/content/renderer/wake_lock/wake_lock_dispatcher.h index 70ce86a12e9..5922318a73e 100644 --- a/chromium/content/renderer/wake_lock/wake_lock_dispatcher.h +++ b/chromium/content/renderer/wake_lock/wake_lock_dispatcher.h @@ -23,7 +23,7 @@ class WakeLockDispatcher : public RenderFrameObserver, // WebWakeLockClient implementation. void requestKeepScreenAwake(bool keepScreenAwake) override; - WakeLockServicePtr wake_lock_service_; + mojom::WakeLockServicePtr wake_lock_service_; }; } // namespace content diff --git a/chromium/content/renderer/webgraphicscontext3d_provider_impl.cc b/chromium/content/renderer/webgraphicscontext3d_provider_impl.cc index 855bd3df44d..28a3327341d 100644 --- a/chromium/content/renderer/webgraphicscontext3d_provider_impl.cc +++ b/chromium/content/renderer/webgraphicscontext3d_provider_impl.cc @@ -5,6 +5,7 @@ #include "content/renderer/webgraphicscontext3d_provider_impl.h" #include "cc/blink/context_provider_web_context.h" +#include "third_party/WebKit/public/platform/callback/WebClosure.h" namespace content { @@ -19,8 +20,17 @@ blink::WebGraphicsContext3D* WebGraphicsContext3DProviderImpl::context3d() { return provider_->WebContext3D(); } +gpu::gles2::GLES2Interface* WebGraphicsContext3DProviderImpl::contextGL() { + return provider_->ContextGL(); +} + GrContext* WebGraphicsContext3DProviderImpl::grContext() { return provider_->GrContext(); } +void WebGraphicsContext3DProviderImpl::setLostContextCallback( + blink::WebClosure c) { + provider_->SetLostContextCallback(c.TakeBaseClosure()); +} + } // namespace content diff --git a/chromium/content/renderer/webgraphicscontext3d_provider_impl.h b/chromium/content/renderer/webgraphicscontext3d_provider_impl.h index 769dd5685c5..7b1d88c5c2a 100644 --- a/chromium/content/renderer/webgraphicscontext3d_provider_impl.h +++ b/chromium/content/renderer/webgraphicscontext3d_provider_impl.h @@ -14,6 +14,12 @@ namespace cc_blink { class ContextProviderWebContext; } +namespace gpu { +namespace gles2 { +class GLES2Interface; +} +} + namespace content { class CONTENT_EXPORT WebGraphicsContext3DProviderImpl @@ -25,7 +31,9 @@ class CONTENT_EXPORT WebGraphicsContext3DProviderImpl // WebGraphicsContext3DProvider implementation. blink::WebGraphicsContext3D* context3d() override; + gpu::gles2::GLES2Interface* contextGL() override; GrContext* grContext() override; + void setLostContextCallback(blink::WebClosure) override; private: scoped_refptr provider_; diff --git a/chromium/content/renderer/websharedworker_proxy.cc b/chromium/content/renderer/websharedworker_proxy.cc index c4439ded6b2..46bbfe17547 100644 --- a/chromium/content/renderer/websharedworker_proxy.cc +++ b/chromium/content/renderer/websharedworker_proxy.cc @@ -7,15 +7,15 @@ #include #include "content/child/webmessageportchannel_impl.h" -#include "content/common/message_router.h" #include "content/common/view_messages.h" #include "content/common/worker_messages.h" +#include "ipc/message_router.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebSharedWorkerClient.h" namespace content { -WebSharedWorkerProxy::WebSharedWorkerProxy(MessageRouter* router, +WebSharedWorkerProxy::WebSharedWorkerProxy(IPC::MessageRouter* router, int route_id) : route_id_(route_id), router_(router), diff --git a/chromium/content/renderer/websharedworker_proxy.h b/chromium/content/renderer/websharedworker_proxy.h index a413c39c6e5..cb6acb991cc 100644 --- a/chromium/content/renderer/websharedworker_proxy.h +++ b/chromium/content/renderer/websharedworker_proxy.h @@ -14,9 +14,11 @@ #include "third_party/WebKit/public/web/WebSharedWorkerConnector.h" #include "url/gurl.h" -namespace content { - +namespace IPC { class MessageRouter; +} + +namespace content { // Implementation of the WebSharedWorker APIs. This object is intended to only // live long enough to allow the caller to send a "connect" event to the worker @@ -27,8 +29,7 @@ class WebSharedWorkerProxy : public blink::WebSharedWorkerConnector, private IPC::Listener { public: // If the worker not loaded yet, route_id == MSG_ROUTING_NONE - WebSharedWorkerProxy(MessageRouter* router, - int route_id); + WebSharedWorkerProxy(IPC::MessageRouter* router, int route_id); ~WebSharedWorkerProxy() override; // Implementations of WebSharedWorkerConnector APIs @@ -62,7 +63,7 @@ class WebSharedWorkerProxy : public blink::WebSharedWorkerConnector, // routing ids). int route_id_; - MessageRouter* const router_; + IPC::MessageRouter* const router_; // Stores messages that were sent before the StartWorkerContext message. std::vector queued_messages_; diff --git a/chromium/content/shell/common/DEPS b/chromium/content/shell/common/DEPS index adb5e51966a..5f52568085a 100644 --- a/chromium/content/shell/common/DEPS +++ b/chromium/content/shell/common/DEPS @@ -1,4 +1,3 @@ include_rules = [ - "+components/test_runner", "+gin/public", ] diff --git a/chromium/content/shell/common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom b/chromium/content/shell/common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom new file mode 100644 index 00000000000..347daa15a04 --- /dev/null +++ b/chromium/content/shell/common/layout_test/layout_test_bluetooth_fake_adapter_setter.mojom @@ -0,0 +1,14 @@ +// 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. + +module content.mojom; + +interface LayoutTestBluetoothFakeAdapterSetter { + + // Sets a mock BluetoothAdapter. The characteristics of the adapter + // depend on |adapter_name|. For a list of available mock adapters + // see layout_test_bluetooth_adapter_provider.h + Set(string adapter_name) => (); + +}; \ No newline at end of file diff --git a/chromium/content/shell/common/layout_test/layout_test_content_client.cc b/chromium/content/shell/common/layout_test/layout_test_content_client.cc new file mode 100644 index 00000000000..da2e9629afa --- /dev/null +++ b/chromium/content/shell/common/layout_test/layout_test_content_client.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 "content/shell/common/layout_test/layout_test_content_client.h" + +#include "content/shell/common/shell_messages.h" + +namespace content { + +bool LayoutTestContentClient::CanSendWhileSwappedOut( + const IPC::Message* message) { + switch (message->type()) { + // Used in layout tests; handled in BlinkTestController. + case ShellViewHostMsg_PrintMessage::ID: + case ShellViewHostMsg_LayoutTestRuntimeFlagsChanged::ID: + return true; + + default: + return false; + } +} + +} // namespace content diff --git a/chromium/content/shell/common/layout_test/layout_test_content_client.h b/chromium/content/shell/common/layout_test/layout_test_content_client.h new file mode 100644 index 00000000000..2f2b4b9e70b --- /dev/null +++ b/chromium/content/shell/common/layout_test/layout_test_content_client.h @@ -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. + +#ifndef CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_CONTENT_CLIENT_H_ +#define CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_CONTENT_CLIENT_H_ + +#include "base/macros.h" +#include "content/shell/common/shell_content_client.h" + +namespace content { + +class LayoutTestContentClient : public ShellContentClient { + public: + LayoutTestContentClient() {} + bool CanSendWhileSwappedOut(const IPC::Message* message) override; + + private: + DISALLOW_COPY_AND_ASSIGN(LayoutTestContentClient); +}; + +} // namespace content + +#endif // CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_CONTENT_CLIENT_H_ diff --git a/chromium/content/shell/common/layout_test/layout_test_messages.h b/chromium/content/shell/common/layout_test/layout_test_messages.h index 97b3073899b..1f420f17304 100644 --- a/chromium/content/shell/common/layout_test/layout_test_messages.h +++ b/chromium/content/shell/common/layout_test/layout_test_messages.h @@ -6,17 +6,17 @@ #include #include -#include "content/public/common/common_param_traits.h" -#include "content/public/common/permission_status.mojom.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_platform_file.h" +#include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h" #include "url/gurl.h" +#include "url/ipc/url_param_traits.h" #define IPC_MESSAGE_START LayoutTestMsgStart -IPC_ENUM_TRAITS_MIN_MAX_VALUE(content::PermissionStatus, - content::PERMISSION_STATUS_GRANTED, - content::PERMISSION_STATUS_ASK) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::mojom::PermissionStatus, + blink::mojom::PermissionStatus::GRANTED, + blink::mojom::PermissionStatus::ASK) IPC_SYNC_MESSAGE_ROUTED1_1(LayoutTestHostMsg_ReadFileToString, base::FilePath /* local path */, @@ -30,14 +30,15 @@ IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_SetDatabaseQuota, IPC_MESSAGE_ROUTED2(LayoutTestHostMsg_SimulateWebNotificationClick, std::string /* title */, int /* action_index */) +IPC_MESSAGE_ROUTED2(LayoutTestHostMsg_SimulateWebNotificationClose, + std::string /* title */, + bool /* by_user */) IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_AcceptAllCookies, bool /* accept */) IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_DeleteAllCookies) IPC_MESSAGE_ROUTED4(LayoutTestHostMsg_SetPermission, std::string /* name */, - content::PermissionStatus /* status */, + blink::mojom::PermissionStatus /* status */, GURL /* origin */, - GURL /* embedding_origin */ ) + GURL /* embedding_origin */) IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ResetPermissions) -IPC_MESSAGE_CONTROL1(LayoutTestHostMsg_SetBluetoothAdapter, - std::string /* name */) diff --git a/chromium/content/shell/common/layout_test/layout_test_switches.cc b/chromium/content/shell/common/layout_test/layout_test_switches.cc new file mode 100644 index 00000000000..bc879a6dee6 --- /dev/null +++ b/chromium/content/shell/common/layout_test/layout_test_switches.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 "content/shell/common/layout_test/layout_test_switches.h" + +#include "base/command_line.h" +#include "base/strings/string_split.h" + +namespace switches { + +// Allow access to external pages during layout tests. +const char kAllowExternalPages[] = "allow-external-pages"; + +// Check whether all system dependencies for running layout tests are met. +const char kCheckLayoutTestSysDeps[] = "check-layout-test-sys-deps"; + +// When specified to "enable-leak-detection" command-line option, +// causes the leak detector to cause immediate crash when found leak. +const char kCrashOnFailure[] = "crash-on-failure"; + +// Enable accelerated 2D canvas. +const char kEnableAccelerated2DCanvas[] = "enable-accelerated-2d-canvas"; + +// Enable font antialiasing for pixel tests. +const char kEnableFontAntialiasing[] = "enable-font-antialiasing"; + +// Always use the complex text path for layout tests. +const char kAlwaysUseComplexText[] = "always-use-complex-text"; + +// Enables the leak detection of loading webpages. This allows us to check +// whether or not reloading a webpage releases web-related objects correctly. +const char kEnableLeakDetection[] = "enable-leak-detection"; + +// Encode binary layout test results (images, audio) using base64. +const char kEncodeBinary[] = "encode-binary"; + +// Request the render trees of pages to be dumped as text once they have +// finished loading. +const char kRunLayoutTest[] = "run-layout-test"; + +// This makes us disable some web-platform runtime features so that we test +// content_shell as if it was a stable release. It is only followed when +// kRunLayoutTest is set. For the features' level, see +// http://dev.chromium.org/blink/runtime-enabled-features. +const char kStableReleaseMode[] = "stable-release-mode"; + +} // namespace switches diff --git a/chromium/content/shell/common/layout_test/layout_test_switches.h b/chromium/content/shell/common/layout_test/layout_test_switches.h new file mode 100644 index 00000000000..33d5494cadc --- /dev/null +++ b/chromium/content/shell/common/layout_test/layout_test_switches.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. + +// Defines all the "layout_test" command-line switches. + +#ifndef CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_SWITCHES_H_ +#define CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_SWITCHES_H_ + +#include +#include + +namespace switches { + +extern const char kAllowExternalPages[]; +extern const char kCheckLayoutTestSysDeps[]; +extern const char kCrashOnFailure[]; +extern const char kEnableAccelerated2DCanvas[]; +extern const char kEnableFontAntialiasing[]; +extern const char kAlwaysUseComplexText[]; +extern const char kEnableLeakDetection[]; +extern const char kEncodeBinary[]; +extern const char kRunLayoutTest[]; +extern const char kStableReleaseMode[]; + +} // namespace switches + +#endif // CONTENT_SHELL_COMMON_LAYOUT_TEST_LAYOUT_TEST_SWITCHES_H_ diff --git a/chromium/content/shell/common/shell_content_client.cc b/chromium/content/shell/common/shell_content_client.cc index d0940b8b92b..82c2f0c5033 100644 --- a/chromium/content/shell/common/shell_content_client.cc +++ b/chromium/content/shell/common/shell_content_client.cc @@ -20,6 +20,19 @@ namespace content { +namespace { + +// This is the public key which the content shell will use to enable origin +// trial features. +// TODO(iclelland): Update this comment with the location of the public and +// private key files when the command-line tool CL lands +static const uint8_t kOriginTrialPublicKey[] = { + 0x75, 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2, + 0x9a, 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f, + 0x64, 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0, +}; +} // namespace + std::string GetShellUserAgent() { std::string product = "Chrome/" CONTENT_SHELL_VERSION; base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -28,16 +41,19 @@ std::string GetShellUserAgent() { return BuildUserAgentFromProduct(product); } -ShellContentClient::~ShellContentClient() { -} +ShellContentClient::ShellContentClient() + : origin_trial_public_key_(base::StringPiece( + reinterpret_cast(kOriginTrialPublicKey), + arraysize(kOriginTrialPublicKey))) {} + +ShellContentClient::~ShellContentClient() {} std::string ShellContentClient::GetUserAgent() const { return GetShellUserAgent(); } base::string16 ShellContentClient::GetLocalizedString(int message_id) const { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kRunLayoutTest)) { + if (switches::IsRunLayoutTestSwitchPresent()) { switch (message_id) { case IDS_FORM_OTHER_DATE_LABEL: return base::ASCIIToUTF16("<>"); @@ -63,8 +79,7 @@ base::string16 ShellContentClient::GetLocalizedString(int message_id) const { base::StringPiece ShellContentClient::GetDataResource( int resource_id, ui::ScaleFactor scale_factor) const { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kRunLayoutTest)) { + if (switches::IsRunLayoutTestSwitchPresent()) { switch (resource_id) { case IDR_BROKENIMAGE: #if defined(OS_MACOSX) @@ -97,4 +112,8 @@ bool ShellContentClient::IsSupplementarySiteIsolationModeEnabled() { switches::kIsolateSitesForTesting); } +base::StringPiece ShellContentClient::GetOriginTrialPublicKey() { + return origin_trial_public_key_; +} + } // namespace content diff --git a/chromium/content/shell/common/shell_content_client.h b/chromium/content/shell/common/shell_content_client.h index ceebc52d2bd..2b1e7b65b10 100644 --- a/chromium/content/shell/common/shell_content_client.h +++ b/chromium/content/shell/common/shell_content_client.h @@ -17,6 +17,7 @@ std::string GetShellUserAgent(); class ShellContentClient : public ContentClient { public: + ShellContentClient(); ~ShellContentClient() override; std::string GetUserAgent() const override; @@ -28,6 +29,10 @@ class ShellContentClient : public ContentClient { int resource_id) const override; gfx::Image& GetNativeImageNamed(int resource_id) const override; bool IsSupplementarySiteIsolationModeEnabled() override; + base::StringPiece GetOriginTrialPublicKey() override; + + private: + base::StringPiece origin_trial_public_key_; }; } // namespace content diff --git a/chromium/content/shell/common/shell_messages.h b/chromium/content/shell/common/shell_messages.h index ebdecb80d8c..f9146b99cc9 100644 --- a/chromium/content/shell/common/shell_messages.h +++ b/chromium/content/shell/common/shell_messages.h @@ -6,6 +6,7 @@ #include #include +#include "base/values.h" #include "content/public/common/common_param_traits.h" #include "content/public/common/page_state.h" #include "content/shell/common/leak_detection_result.h" @@ -14,6 +15,7 @@ #include "ipc/ipc_platform_file.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/ipc/gfx_param_traits.h" +#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" #define IPC_MESSAGE_START ShellMsgStart @@ -34,12 +36,23 @@ IPC_MESSAGE_ROUTED0(ShellViewMsg_Reset) IPC_MESSAGE_CONTROL1(ShellViewMsg_SetWebKitSourceDir, base::FilePath /* webkit source dir */) -// Sets the initial configuration to use for layout tests. +// Sets the test config for a layout test that is being started. IPC_MESSAGE_ROUTED1(ShellViewMsg_SetTestConfiguration, content::ShellTestConfiguration) -// Tells the main window that a secondary window in a different process invoked -// notifyDone(). +// Replicates test config (for an already started test) to a new renderer. +IPC_MESSAGE_ROUTED2( + ShellViewMsg_ReplicateTestConfiguration, + content::ShellTestConfiguration, + base::DictionaryValue /* accumulated_layout_test_runtime_flags_changes */) + +// Used to broadcast changes happening in one renderer to all other renderers. +IPC_MESSAGE_ROUTED1( + ShellViewMsg_ReplicateLayoutTestRuntimeFlagsChanges, + base::DictionaryValue /* changed_layout_test_runtime_flags */) + +// Tells the main window that a secondary renderer in a different process thinks +// the test is finished. IPC_MESSAGE_ROUTED0(ShellViewMsg_NotifyDone) // Pushes a snapshot of the current session history from the browser process. @@ -54,10 +67,33 @@ IPC_MESSAGE_ROUTED3( IPC_MESSAGE_ROUTED0(ShellViewMsg_TryLeakDetection) +// Asks a frame to dump its contents into a string and send them back over IPC. +IPC_MESSAGE_ROUTED0(ShellViewMsg_LayoutDumpRequest) + +// Notifies BlinkTestRunner that the layout dump has completed +// (and that it can proceed with finishing up the test). +IPC_MESSAGE_ROUTED1(ShellViewMsg_LayoutDumpCompleted, + std::string /* completed/stitched layout dump */) + +// Notifies the browser that one of renderers has changed layout test runtime +// flags (i.e. has set dump_as_text). +IPC_MESSAGE_ROUTED1( + ShellViewHostMsg_LayoutTestRuntimeFlagsChanged, + base::DictionaryValue /* changed_layout_test_runtime_flags */) + // Send a text dump of the WebContents to the render host. IPC_MESSAGE_ROUTED1(ShellViewHostMsg_TextDump, std::string /* dump */) +// Asks the browser process to perform a layout dump spanning all the +// (potentially cross-process) frames. This triggers multiple +// ShellViewMsg_LayoutDumpRequest / ShellViewHostMsg_LayoutDumpResponse messages +// and ends with sending of ShellViewMsg_LayoutDumpCompleted. +IPC_MESSAGE_ROUTED0(ShellViewHostMsg_InitiateLayoutDump) + +// Sends a layout dump of a frame (response to ShellViewMsg_LayoutDumpRequest). +IPC_MESSAGE_ROUTED1(ShellViewHostMsg_LayoutDumpResponse, std::string /* dump */) + // Send an image dump of the WebContents to the render host. IPC_MESSAGE_ROUTED2(ShellViewHostMsg_ImageDump, std::string /* actual pixel hash */, @@ -71,7 +107,7 @@ IPC_MESSAGE_ROUTED0(ShellViewHostMsg_TestFinished) IPC_MESSAGE_ROUTED0(ShellViewHostMsg_ResetDone) -IPC_MESSAGE_ROUTED0(ShellViewHostMsg_TestFinishedInSecondaryWindow) +IPC_MESSAGE_ROUTED0(ShellViewHostMsg_TestFinishedInSecondaryRenderer) // WebTestDelegate related. IPC_MESSAGE_ROUTED1(ShellViewHostMsg_OverridePreferences, @@ -82,6 +118,9 @@ IPC_MESSAGE_ROUTED0(ShellViewHostMsg_ClearDevToolsLocalStorage) IPC_MESSAGE_ROUTED2(ShellViewHostMsg_ShowDevTools, std::string /* settings */, std::string /* frontend_url */) +IPC_MESSAGE_ROUTED2(ShellViewHostMsg_EvaluateInDevTools, + int /* call_id */, + std::string /* script */) IPC_MESSAGE_ROUTED0(ShellViewHostMsg_CloseDevTools) IPC_MESSAGE_ROUTED1(ShellViewHostMsg_GoToOffset, int /* offset */) diff --git a/chromium/content/shell/common/shell_switches.cc b/chromium/content/shell/common/shell_switches.cc index 25c1070e4eb..cf4280b415a 100644 --- a/chromium/content/shell/common/shell_switches.cc +++ b/chromium/content/shell/common/shell_switches.cc @@ -6,15 +6,10 @@ #include "base/command_line.h" #include "base/strings/string_split.h" +#include "content/shell/common/layout_test/layout_test_switches.h" namespace switches { -// Allow access to external pages during layout tests. -const char kAllowExternalPages[] = "allow-external-pages"; - -// Check whether all system dependencies for running layout tests are met. -const char kCheckLayoutTestSysDeps[] = "check-layout-test-sys-deps"; - // Tells Content Shell that it's running as a content_browsertest. const char kContentBrowserTest[] = "browser-test"; @@ -24,30 +19,6 @@ const char kContentShellDataPath[] = "data-path"; // The directory breakpad should store minidumps in. const char kCrashDumpsDir[] = "crash-dumps-dir"; -// When specified to "enable-leak-detection" command-line option, -// causes the leak detector to cause immediate crash when found leak. -const char kCrashOnFailure[] = "crash-on-failure"; - -// When run-layout-test is enabled, this causes the line box tree for -// each LayoutBlockFlow to be dumped as well. -const char kDumpLineBoxTrees[] = "dump-line-box-trees"; - -// Enable accelerated 2D canvas. -const char kEnableAccelerated2DCanvas[] = "enable-accelerated-2d-canvas"; - -// Enable font antialiasing for pixel tests. -const char kEnableFontAntialiasing[] = "enable-font-antialiasing"; - -// Always use the complex text path for layout tests. -const char kAlwaysUseComplexText[] = "always-use-complex-text"; - -// Enables the leak detection of loading webpages. This allows us to check -// whether or not reloading a webpage releases web-related objects correctly. -const char kEnableLeakDetection[] = "enable-leak-detection"; - -// Encode binary layout test results (images, audio) using base64. -const char kEncodeBinary[] = "encode-binary"; - // Exposes the window.internals object to JavaScript for interactive development // and debugging of layout tests that rely on it. const char kExposeInternalsForTesting[] = "expose-internals-for-testing"; @@ -65,16 +36,6 @@ const char kIsolateSitesForTesting[] = "isolate-sites-for-testing"; // with a semicolon (;). const char kRegisterFontFiles[] = "register-font-files"; -// Request the render trees of pages to be dumped as text once they have -// finished loading. -const char kRunLayoutTest[] = "run-layout-test"; - -// This makes us disable some web-platform runtime features so that we test -// content_shell as if it was a stable release. It is only followed when -// kRunLayoutTest is set. For the features' level, see -// http://dev.chromium.org/blink/runtime-enabled-features. -const char kStableReleaseMode[] = "stable-release-mode"; - // Size for the content_shell's host window (i.e. "800x600"). const char kContentShellHostWindowSize[] = "content-shell-host-window-size"; @@ -90,4 +51,9 @@ std::vector GetSideloadFontFiles() { return files; } +bool IsRunLayoutTestSwitchPresent() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kRunLayoutTest); +} + } // namespace switches diff --git a/chromium/content/shell/common/shell_switches.h b/chromium/content/shell/common/shell_switches.h index db786144b7f..4b096224646 100644 --- a/chromium/content/shell/common/shell_switches.h +++ b/chromium/content/shell/common/shell_switches.h @@ -12,28 +12,23 @@ namespace switches { -extern const char kAllowExternalPages[]; -extern const char kCheckLayoutTestSysDeps[]; extern const char kContentBrowserTest[]; extern const char kContentShellDataPath[]; extern const char kCrashDumpsDir[]; -extern const char kCrashOnFailure[]; -extern const char kDumpLineBoxTrees[]; -extern const char kEnableAccelerated2DCanvas[]; -extern const char kEnableFontAntialiasing[]; -extern const char kAlwaysUseComplexText[]; -extern const char kEnableLeakDetection[]; -extern const char kEncodeBinary[]; extern const char kExposeInternalsForTesting[]; extern const char kIsolateSitesForTesting[]; extern const char kRegisterFontFiles[]; -extern const char kRunLayoutTest[]; -extern const char kStableReleaseMode[]; extern const char kContentShellHostWindowSize[]; // Returns list of extra font files to be made accessible to the renderer. std::vector GetSideloadFontFiles(); +// Tells if content shell is running layout tests. +// TODO(lukasza): The function below somewhat violates the layering (by +// enabling shell -> layout_tests dependency) but at least narrows the extent of +// the dependency to a single switch... +bool IsRunLayoutTestSwitchPresent(); + } // namespace switches #endif // CONTENT_SHELL_COMMON_SHELL_SWITCHES_H_ diff --git a/chromium/content/shell/shell_resources.grd b/chromium/content/shell/shell_resources.grd new file mode 100644 index 00000000000..bfb4071d2f5 --- /dev/null +++ b/chromium/content/shell/shell_resources.grd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/chromium/content/utility/BUILD.gn b/chromium/content/utility/BUILD.gn index 8bb058aeb12..0ef4261ba32 100644 --- a/chromium/content/utility/BUILD.gn +++ b/chromium/content/utility/BUILD.gn @@ -7,8 +7,11 @@ import("//media/media_options.gni") source_set("utility") { # Only the public target should depend on this. All other targets (even - # internal content ones) should depend on the public one. - visibility = [ "//content/public/utility:utility_sources" ] + # internal content ones other than test) should depend on the public one. + visibility = [ + ":for_content_tests", + "//content/public/utility:utility_sources", + ] sources = rebase_path(content_utility_gypi_values.utility_sources, ".", "//content") @@ -17,11 +20,13 @@ source_set("utility") { deps = [ "//base", + "//components/scheduler", "//content:export", "//content/public/child:child_sources", "//content/public/common:common_sources", "//content/public/common:mojo_bindings", "//courgette:courgette_lib", + "//media/mojo/services:application_factory", "//mojo/common", "//mojo/public/cpp/bindings", "//mojo/shell", @@ -31,7 +36,17 @@ source_set("utility") { "//url", ] - if (enable_mojo_media == "utility") { - deps += [ "//media/mojo/services:application" ] + if (mojo_media_host == "utility") { + deps += [ "//media/mojo/services:application_factory" ] + } +} + +# See comment at the top of //content/BUILD.gn for how this works. +group("for_content_tests") { + visibility = [ "//content/test/*" ] + if (!is_component_build) { + public_deps = [ + ":utility", + ] } } diff --git a/chromium/content/utility/in_process_utility_thread.h b/chromium/content/utility/in_process_utility_thread.h index 850e015a48f..074837b942c 100644 --- a/chromium/content/utility/in_process_utility_thread.h +++ b/chromium/content/utility/in_process_utility_thread.h @@ -8,6 +8,7 @@ #include #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/threading/thread.h" #include "content/common/content_export.h" #include "content/common/in_process_child_thread_params.h" diff --git a/chromium/content/utility/utility_blink_platform_impl.h b/chromium/content/utility/utility_blink_platform_impl.h index 3af30519731..d66299eca6f 100644 --- a/chromium/content/utility/utility_blink_platform_impl.h +++ b/chromium/content/utility/utility_blink_platform_impl.h @@ -6,6 +6,7 @@ #define CONTENT_UTILITY_UTILITY_BLINK_PLATFORM_IMPL_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "content/child/blink_platform_impl.h" namespace content { diff --git a/chromium/content/utility/utility_process_control_impl.cc b/chromium/content/utility/utility_process_control_impl.cc index 1a886e1abb8..f717f868795 100644 --- a/chromium/content/utility/utility_process_control_impl.cc +++ b/chromium/content/utility/utility_process_control_impl.cc @@ -5,14 +5,14 @@ #include "content/utility/utility_process_control_impl.h" #include "base/bind.h" +#include "content/common/mojo/static_loader.h" #include "content/public/common/content_client.h" #include "content/public/utility/content_utility_client.h" #include "content/public/utility/utility_thread.h" #include "content/utility/utility_thread_impl.h" -#include "mojo/shell/static_application_loader.h" #if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS) -#include "media/mojo/services/mojo_media_application.h" +#include "media/mojo/services/mojo_media_application_factory.h" #endif namespace content { @@ -30,22 +30,21 @@ UtilityProcessControlImpl::UtilityProcessControlImpl() {} UtilityProcessControlImpl::~UtilityProcessControlImpl() {} -void UtilityProcessControlImpl::RegisterApplicationLoaders( - URLToLoaderMap* url_to_loader_map) { - URLToLoaderMap& map_ref = *url_to_loader_map; +void UtilityProcessControlImpl::RegisterLoaders( + NameToLoaderMap* name_to_loader_map) { + NameToLoaderMap& map_ref = *name_to_loader_map; ContentUtilityClient::StaticMojoApplicationMap apps; GetContentClient()->utility()->RegisterMojoApplications(&apps); for (const auto& entry : apps) { - map_ref[entry.first] = new mojo::shell::StaticApplicationLoader( - entry.second, base::Bind(&QuitProcess)); + map_ref[entry.first] = + new StaticLoader(entry.second, base::Bind(&QuitProcess)); } #if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS) - map_ref[GURL("mojo:media")] = new mojo::shell::StaticApplicationLoader( - base::Bind(&media::MojoMediaApplication::CreateApp), - base::Bind(&QuitProcess)); + map_ref["mojo:media"] = new StaticLoader( + base::Bind(&media::CreateMojoMediaApplication), base::Bind(&QuitProcess)); #endif } diff --git a/chromium/content/utility/utility_process_control_impl.h b/chromium/content/utility/utility_process_control_impl.h index f57dbe6ebf3..9d450fd2fda 100644 --- a/chromium/content/utility/utility_process_control_impl.h +++ b/chromium/content/utility/utility_process_control_impl.h @@ -18,7 +18,7 @@ class UtilityProcessControlImpl : public ProcessControlImpl { ~UtilityProcessControlImpl() override; // ProcessControlImpl: - void RegisterApplicationLoaders(URLToLoaderMap* url_to_loader_map) override; + void RegisterLoaders(NameToLoaderMap* name_to_loader_map) override; private: void OnLoadFailed() override; diff --git a/chromium/content/utility/utility_thread_impl.cc b/chromium/content/utility/utility_thread_impl.cc index b6587f58aa6..edbf498303a 100644 --- a/chromium/content/utility/utility_thread_impl.cc +++ b/chromium/content/utility/utility_thread_impl.cc @@ -57,7 +57,7 @@ void UtilityThreadImpl::Shutdown() { ChildThreadImpl::Shutdown(); if (blink_platform_impl_) - blink::shutdownWithoutV8(); + blink::Platform::shutdown(); } void UtilityThreadImpl::ReleaseProcessIfNeeded() { @@ -85,7 +85,7 @@ void UtilityThreadImpl::EnsureBlinkInitialized() { } blink_platform_impl_.reset(new UtilityBlinkPlatformImpl); - blink::initializeWithoutV8(blink_platform_impl_.get()); + blink::Platform::initialize(blink_platform_impl_.get()); } void UtilityThreadImpl::Init() { @@ -108,9 +108,6 @@ bool UtilityThreadImpl::OnControlMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(UtilityThreadImpl, msg) IPC_MESSAGE_HANDLER(UtilityMsg_BatchMode_Started, OnBatchModeStarted) IPC_MESSAGE_HANDLER(UtilityMsg_BatchMode_Finished, OnBatchModeFinished) -#if defined(OS_POSIX) && defined(ENABLE_PLUGINS) - IPC_MESSAGE_HANDLER(UtilityMsg_LoadPlugins, OnLoadPlugins) -#endif IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -125,30 +122,8 @@ void UtilityThreadImpl::OnBatchModeFinished() { ReleaseProcessIfNeeded(); } -#if defined(OS_POSIX) && defined(ENABLE_PLUGINS) -void UtilityThreadImpl::OnLoadPlugins( - const std::vector& plugin_paths) { - PluginList* plugin_list = PluginList::Singleton(); - - std::vector plugins; - // TODO(bauerb): If we restart loading plugins, we might mess up the logic in - // PluginList::ShouldLoadPlugin due to missing the previously loaded plugins - // in |plugin_groups|. - for (size_t i = 0; i < plugin_paths.size(); ++i) { - WebPluginInfo plugin; - if (!plugin_list->LoadPluginIntoPluginList( - plugin_paths[i], &plugins, &plugin)) - Send(new UtilityHostMsg_LoadPluginFailed(i, plugin_paths[i])); - else - Send(new UtilityHostMsg_LoadedPlugin(i, plugin)); - } - - ReleaseProcessIfNeeded(); -} -#endif - void UtilityThreadImpl::BindProcessControlRequest( - mojo::InterfaceRequest request) { + mojo::InterfaceRequest request) { DCHECK(process_control_); process_control_bindings_.AddBinding(process_control_.get(), std::move(request)); diff --git a/chromium/content/utility/utility_thread_impl.h b/chromium/content/utility/utility_thread_impl.h index 415b2af95e6..2894b3d99ae 100644 --- a/chromium/content/utility/utility_thread_impl.h +++ b/chromium/content/utility/utility_thread_impl.h @@ -16,7 +16,7 @@ #include "content/common/content_export.h" #include "content/common/process_control.mojom.h" #include "content/public/utility/utility_thread.h" -#include "mojo/common/weak_binding_set.h" +#include "mojo/public/cpp/bindings/binding_set.h" namespace base { class FilePath; @@ -56,12 +56,8 @@ class UtilityThreadImpl : public UtilityThread, void OnBatchModeStarted(); void OnBatchModeFinished(); -#if defined(OS_POSIX) && defined(ENABLE_PLUGINS) - void OnLoadPlugins(const std::vector& plugin_paths); -#endif - void BindProcessControlRequest( - mojo::InterfaceRequest request); + mojo::InterfaceRequest request); // True when we're running in batch mode. bool batch_mode_; @@ -71,8 +67,8 @@ class UtilityThreadImpl : public UtilityThread, // Process control for Mojo application hosting. scoped_ptr process_control_; - // Bindings to the ProcessControl impl. - mojo::WeakBindingSet process_control_bindings_; + // Bindings to the mojom::ProcessControl impl. + mojo::BindingSet process_control_bindings_; DISALLOW_COPY_AND_ASSIGN(UtilityThreadImpl); }; diff --git a/chromium/content/utility/webthread_impl_for_utility_thread.cc b/chromium/content/utility/webthread_impl_for_utility_thread.cc index f72456d7712..9ee10aa8a24 100644 --- a/chromium/content/utility/webthread_impl_for_utility_thread.cc +++ b/chromium/content/utility/webthread_impl_for_utility_thread.cc @@ -25,13 +25,13 @@ blink::PlatformThreadId WebThreadImplForUtilityThread::threadId() const { return thread_id_; } -base::SingleThreadTaskRunner* WebThreadImplForUtilityThread::TaskRunner() +base::SingleThreadTaskRunner* WebThreadImplForUtilityThread::GetTaskRunner() const { return task_runner_.get(); } scheduler::SingleThreadIdleTaskRunner* -WebThreadImplForUtilityThread::IdleTaskRunner() const { +WebThreadImplForUtilityThread::GetIdleTaskRunner() const { NOTIMPLEMENTED(); return nullptr; } diff --git a/chromium/content/utility/webthread_impl_for_utility_thread.h b/chromium/content/utility/webthread_impl_for_utility_thread.h index d2ad637b17e..a5220fe0960 100644 --- a/chromium/content/utility/webthread_impl_for_utility_thread.h +++ b/chromium/content/utility/webthread_impl_for_utility_thread.h @@ -21,8 +21,8 @@ class WebThreadImplForUtilityThread : public scheduler::WebThreadBase { blink::PlatformThreadId threadId() const override; // WebThreadBase implementation. - base::SingleThreadTaskRunner* TaskRunner() const override; - scheduler::SingleThreadIdleTaskRunner* IdleTaskRunner() const override; + base::SingleThreadTaskRunner* GetTaskRunner() const override; + scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override; private: scoped_refptr task_runner_; diff --git a/chromium/content/zygote/zygote_linux.cc b/chromium/content/zygote/zygote_linux.cc index 71d94da8cdf..09828fb4421 100644 --- a/chromium/content/zygote/zygote_linux.cc +++ b/chromium/content/zygote/zygote_linux.cc @@ -46,7 +46,7 @@ #include "sandbox/linux/services/credentials.h" #include "sandbox/linux/services/namespace_sandbox.h" -// See http://code.google.com/p/chromium/wiki/LinuxZygote +// See https://chromium.googlesource.com/chromium/src/+/master/docs/linux_zygote.md namespace content { @@ -108,7 +108,7 @@ bool Zygote::ProcessRequests() { // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the // browser on it. // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel. - // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC + // See https://chromium.googlesource.com/chromium/src/+/master/docs/linux_sandbox_ipc.md // We need to accept SIGCHLD, even though our handler is a no-op because // otherwise we cannot wait on children. (According to POSIX 2001.) @@ -237,14 +237,14 @@ bool Zygote::HandleRequestFromBrowser(int fd) { if (len == 0 || (len == -1 && errno == ECONNRESET)) { // EOF from the browser. We should die. - // TODO(earthdok): call __sanititizer_cov_dump() here to obtain code + // TODO(eugenis): call __sanititizer_cov_dump() here to obtain code // coverage for the Zygote. Currently it's not possible because of // confusion over who is responsible for closing the file descriptor. for (int fd : extra_fds_) { PCHECK(0 == IGNORE_EINTR(close(fd))); } #if !defined(SANITIZER_COVERAGE) - // TODO(earthdok): add watchdog thread before using this in builds not + // TODO(eugenis): add watchdog thread before using this in builds not // using sanitizer coverage. CHECK(extra_children_.empty()); #endif @@ -300,7 +300,6 @@ bool Zygote::HandleRequestFromBrowser(int fd) { return false; } -// TODO(jln): remove callers to this broken API. See crbug.com/274855. void Zygote::HandleReapRequest(int fd, base::PickleIterator iter) { base::ProcessId child; @@ -436,9 +435,15 @@ int Zygote::ForkWithRealPid(const std::string& process_type, DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; return -1; } + int mojo_channel_fd = LookUpFd(fd_mapping, kMojoIPCChannel); + if (mojo_channel_fd < 0) { + DLOG(ERROR) << "Failed to find kMojoIPCChannel in FD mapping"; + return -1; + } std::vector fds; fds.push_back(ipc_channel_fd); // kBrowserFDIndex fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex + fds.push_back(mojo_channel_fd); // kMojoParentFDIndex pid = helper->Fork(process_type, fds, channel_id); // Helpers should never return in the child process. diff --git a/chromium/content/zygote/zygote_main_linux.cc b/chromium/content/zygote/zygote_main_linux.cc index 8a3221a38dd..da5a1e8299f 100644 --- a/chromium/content/zygote/zygote_main_linux.cc +++ b/chromium/content/zygote/zygote_main_linux.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -46,8 +47,10 @@ #include "sandbox/linux/services/namespace_sandbox.h" #include "sandbox/linux/services/thread_helpers.h" #include "sandbox/linux/suid/client/setuid_sandbox_client.h" +#include "third_party/WebKit/public/web/linux/WebFontRendering.h" #include "third_party/icu/source/i18n/unicode/timezone.h" #include "third_party/skia/include/ports/SkFontConfigInterface.h" +#include "third_party/skia/include/ports/SkFontMgr_android.h" #if defined(OS_LINUX) #include @@ -84,7 +87,7 @@ void RunTwoClosures(const base::Closure* first, const base::Closure* second) { } // namespace -// See http://code.google.com/p/chromium/wiki/LinuxZygote +// See https://chromium.googlesource.com/chromium/src/+/master/docs/linux_zygote.md static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, char* timezone_out, @@ -148,7 +151,7 @@ static bool g_am_zygote_or_renderer = false; // // Our replacement functions can check this global and either proxy // the call to the browser over the sandbox IPC -// (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use +// (https://chromium.googlesource.com/chromium/src/+/master/docs/linux_sandbox_ipc.md) or they can use // dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the // current module. // @@ -333,6 +336,12 @@ static void ZygotePreSandboxInit() { // cached and there's no more need to access the file system. scoped_ptr zone(icu::TimeZone::createDefault()); +#if defined(ARCH_CPU_ARM_FAMILY) + // On ARM, BoringSSL requires access to /proc/cpuinfo to determine processor + // features. Query this before entering the sandbox. + CRYPTO_library_init(); +#endif + // Pass BoringSSL a copy of the /dev/urandom file descriptor so RAND_bytes // will work inside the sandbox. RAND_set_urandom_fd(base::GetUrandomFD()); @@ -344,8 +353,32 @@ static void ZygotePreSandboxInit() { #if defined(ENABLE_WEBRTC) InitializeWebRtcModule(); #endif + SkFontConfigInterface::SetGlobal( new FontConfigIPC(GetSandboxFD()))->unref(); + + // Set the android SkFontMgr for blink. We need to ensure this is done + // before the sandbox is initialized to allow the font manager to access + // font configuration files on disk. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAndroidFontsPath)) { + std::string android_fonts_dir = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAndroidFontsPath); + + if (android_fonts_dir.size() > 0 && android_fonts_dir.back() != '/') + android_fonts_dir += '/'; + std::string font_config = android_fonts_dir + "fonts.xml"; + SkFontMgr_Android_CustomFonts custom; + custom.fSystemFontUse = + SkFontMgr_Android_CustomFonts::SystemFontUse::kOnlyCustom; + custom.fBasePath = android_fonts_dir.c_str(); + custom.fFontsXml = font_config.c_str(); + custom.fFallbackFontsXml = nullptr; + custom.fIsolated = true; + + blink::WebFontRendering::setSkiaFontManager(SkFontMgr_New_Android(&custom)); + } } static bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) { -- cgit v1.2.1